Java 线程状态转换(WAITING -> RUNNABLE 触发条件)

林欢喜 Java经验 发布时间:2025-12-25 15:10:58 阅读数:16571 1
下文笔者讲述java中线程状态转换相关说明,如下所示

WAITING 与 RUNNABLE 状态简介

  1. WAITING(无时限等待):
          线程处于主动等待状态,无超时时间限制,
       若没有外部触发条件,会永久等待,无法自行唤醒,
       此时线程不会被 CPU 调度,释放CPU执行权和锁资源
  2. RUNNABLE(可运行/运行):
      线程处于就绪状态(等待CPU调度)
        或
      运行状态(正在执行任务)
      是线程执行业务逻辑的核心状态,由 JVM 线程调度器负责调度。
   
     WAITING -> RUNNABLE 的转换,
      本质是外部触发线程唤醒条件,
       使线程结束等待,
       进入就绪队列等待CPU调度

线程状态转换的核心触发条件

   Java中线程进入WAITING状态的场景固定,
     对应的唤醒条件(即 WAITING -> RUNNABLE 转换条件)也一一对应, 
      核心分为 5 大类场景:

     场景1:
        Object.wait() 等待 → Object.notify()/notifyAll() 唤醒
          这是最基础的等待-唤醒机制,基于对象监视器(synchronized 锁)实现,
         线程通过 `Object.wait()` 进入WAITING 状态,
         需通过其他线程调用同一对象的 `notify()` 或 `notifyAll()` 唤醒。

   核心触发条件:
     1.等待线程:
         必须在 synchronized 同步代码块/方法中
           调用 `Object.wait()`(释放对象锁,进入该对象的等待队列,变为 WAITING 状态)
  
     2.唤醒线程:
         必须在 同一对象的 synchronized 同步代码块/方法中 调用 `object.notify()`
          (唤醒等待队列中的任意一个线程)
       或 `object.notifyAll()`(唤醒等待队列中的所有线程);
     3.唤醒后:被唤醒的线程不会立即变为 RUNNABLE,
           而是先重新竞争该对象的 synchronized 锁,
           竞争成功后才会从 WAITING 转为 RUNNABLE

示例

 
public class WaitNotifyDemo {
    // 共享对象(作为锁对象和等待队列载体)
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 等待线程
        Thread waitThread = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":进入WAITING状态,释放锁");
                    LOCK.wait(); // 进入WAITING状态,释放LOCK锁
                    System.out.println(Thread.currentThread().getName() + ":被唤醒,转为RUNNABLE状态");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "等待线程");

        // 唤醒线程
        Thread notifyThread = new Thread(() -> {
            synchronized (LOCK) {
                System.out.println(Thread.currentThread().getName() + ":获取锁,准备唤醒等待线程");
                LOCK.notify(); // 唤醒LOCK对象等待队列中的一个线程
                // LOCK.notifyAll(); // 唤醒所有等待线程
                System.out.println(Thread.currentThread().getName() + ":唤醒操作完成,即将释放锁");
            }
        }, "唤醒线程");

        waitThread.start();
        Thread.sleep(1000); // 确保等待线程先进入WAITING状态
        notifyThread.start();

        waitThread.join();
        notifyThread.join();
        System.out.println("执行完成");
    }
}

运行结果:

等待线程:
     进入WAITING状态,释放锁
唤醒线程:
    获取锁,准备唤醒等待线程
唤醒线程:
    唤醒操作完成,即将释放锁
等待线程:
   被唤醒,转为RUNNABLE状态
执行完成

场景2:
Thread.join() 等待 → 目标线程执行完毕唤醒

当线程A调用线程 B 的 `join()` 方法时
   线程 A 会进入 WAITING 状态,
    等待线程 B 执行完毕,
     线程 B 终止后,
    线程 A 会自动从 WAITING 转为 RUNNABLE

 核心触发条件:
   1.等待线程(A):调用目标线程(B
     `Thread.join()`(无参方法,无超时等待),进入 WAITING 状态;
   2.唤醒触发:当目标线程(B)执行完毕(正常终止或异常终止),
      JVM 会自动唤醒线程 A,使其从 WAITING 转为 RUNNABLE;
   3.补充:`join(long millis)` 是 TIMED_WAITING 状态,
     并非 WAITING,超时后会自动唤醒。

public class ThreadJoinDemo {
    public static void main(String[] args) throws InterruptedException {
        // 目标线程(被join的线程)
        Thread targetThread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":开始执行,耗时3秒");
            try {
                Thread.sleep(3000); // 模拟业务执行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":执行完毕,即将终止");
        }, "目标线程");

        // 等待线程(主线程中创建)
        Thread waitThread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":等待目标线程执行完毕");
            try {
                targetThread.join(); // 调用join(),进入WAITING状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + ":目标线程已终止,转为RUNNABLE状态");
        }, "等待线程");

        targetThread.start();
        waitThread.start();

        waitThread.join();
        System.out.println("主线程执行完成");
    }
}
  
 运行结果:

目标线程:开始执行,耗时3秒
等待线程:等待目标线程执行完毕
目标线程:执行完毕,即将终止
等待线程:目标线程已终止,转为RUNNABLE状态
主线程执行完成
 

场景3:LockSupport.park() 等待 → LockSupport.unpark(Thread) 唤醒

`LockSupport` 是 JDK 提供的底层线程阻塞/唤醒工具,
    无需依赖 synchronized 锁或 Lock 锁,线程通过 `LockSupport.park()` 进入 WAITING 状态,
   需通过其他线程调用 `LockSupport.unpark(目标线程)` 唤醒。

 核心触发条件:
   1.等待线程:调用 `LockSupport.park()`(无参方法,无超时等待),
      进入 WAITING 状态(称为“停车”),无需持有任何锁;
   2.唤醒线程:调用 `LockSupport.unpark(Thread target)`,
     指定唤醒目标线程(称为“解锁停车”),无需在同步块中执行;
   3.特性:`unpark()` 可在 `park()` 之前调用(提前“许可”),
      线程后续调用 `park()` 时会直接跳过等待,不会进入 WAITING 状态;
     唤醒后线程直接转为 RUNNABLE,无需竞争锁。
import java.util.concurrent.locks.LockSupport;

public class LockSupportParkUnparkDemo {
    public static void main(String[] args) throws InterruptedException {
        // 等待线程
        Thread parkThread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":准备进入WAITING状态");
            LockSupport.park(); // 停车,进入WAITING状态
            System.out.println(Thread.currentThread().getName() + ":被唤醒,转为RUNNABLE状态");
        }, "等待线程(Park)");

        parkThread.start();
        Thread.sleep(2000); // 确保等待线程进入WAITING状态

        // 唤醒线程
        Thread unparkThread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":准备唤醒等待线程");
            LockSupport.unpark(parkThread); // 解锁停车,唤醒目标线程
            System.out.println(Thread.currentThread().getName() + ":唤醒操作完成");
        }, "唤醒线程(Unpark)");

        unparkThread.start();

        parkThread.join();
        unparkThread.join();
        System.out.println("执行完成");
    }
}

运行结果

等待线程(Park):准备进入WAITING状态
唤醒线程(Unpark):准备唤醒等待线程
唤醒线程(Unpark):唤醒操作完成
等待线程(Park):被唤醒,转为RUNNABLE状态
执行完成

场景4:
Condition.await() 等待 → Condition.signal()/signalAll() 唤醒

`Condition` 是基于 `Lock` 锁的条件等待机制,
   替代了 `Object.wait()`/`notify()`,功能更灵活,
    线程通过 `Condition.await()` 进入 WAITING 状态,
    需通过其他线程调用同一 `Condition` 的 `signal()`/`signalAll()` 唤醒。

 核心触发条件:
    1. 等待线程:必须在 Lock 锁获取之后(`lock.lock()` 之后)
     调用 `condition.await()`(释放 Lock 锁,进入 Condition 的等待队列,变为 WAITING 状态);
    2.唤醒线程:必须在 同一 Lock 锁获取之后 调用 `condition.signal()`
     (唤醒 Condition 等待队列中的一个线程)或 `condition.signalAll()`(唤醒所有等待线程);
    3.唤醒后:被唤醒的线程需重新竞争 Lock 锁,
       竞争成功后才会从 WAITING 转为 RUNNABLE,继续执行后续逻辑。
例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionAwaitSignalDemo {
    // 可重入锁
    private static final Lock LOCK = new ReentrantLock();
    // 基于Lock创建Condition条件对象
    private static final Condition CONDITION = LOCK.newCondition();

    public static void main(String[] args) throws InterruptedException {
        // 等待线程
        Thread awaitThread = new Thread(() -> {
            LOCK.lock(); // 获取Lock锁
            try {
                System.out.println(Thread.currentThread().getName() + ":进入WAITING状态,释放Lock锁");
                CONDITION.await(); // 进入Condition等待队列,变为WAITING状态
                System.out.println(Thread.currentThread().getName() + ":被唤醒,转为RUNNABLE状态");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                LOCK.unlock(); // 释放Lock锁
                System.out.println(Thread.currentThread().getName() + ":释放Lock锁");
            }
        }, "等待线程(Await)");

        // 唤醒线程
        Thread signalThread = new Thread(() -> {
            LOCK.lock(); // 获取Lock锁
            try {
                System.out.println(Thread.currentThread().getName() + ":获取Lock锁,准备唤醒等待线程");
                CONDITION.signal(); // 唤醒Condition等待队列中的一个线程
                // CONDITION.signalAll(); // 唤醒所有等待线程
                System.out.println(Thread.currentThread().getName() + ":唤醒操作完成");
            } finally {
                LOCK.unlock(); // 释放Lock锁
                System.out.println(Thread.currentThread().getName() + ":释放Lock锁");
            }
        }, "唤醒线程(Signal)");

        awaitThread.start();
        Thread.sleep(1000); // 确保等待线程先进入WAITING状态
        signalThread.start();

        awaitThread.join();
        signalThread.join();
        System.out.println("执行完成");
    }
}

运行结果

等待线程(Await):进入WAITING状态,释放Lock锁
唤醒线程(Signal):获取Lock锁,准备唤醒等待线程
唤醒线程(Signal):唤醒操作完成
唤醒线程(Signal):释放Lock锁
等待线程(Await):被唤醒,转为RUNNABLE状态
等待线程(Await):释放Lock锁
执行完成

场景5:线程中断(InterruptedException)唤醒

无论线程是通过 `Object.wait()`、`Condition.await()` 还是 `Thread.join()` 进入 WAITING 状态,
若其他线程调用该等待线程的 `interrupt()` 方法,会触发 `InterruptedException` 异常,
线程会从 WAITING 状态被唤醒,转为 RUNNABLE 状态(实际是先进入异常处理流程,最终转为 RUNNABLE)。

 核心触发条件:
    1.等待线程:处于 WAITING 状态(由 `wait()`/`await()`/`join()` 触发);
    2.中断线程:调用等待线程的 `Thread.interrupt()` 方法,设置中断标志位;
    3.唤醒结果:等待线程抛出 `InterruptedException` 异常,中断标志位被清除,
    线程从 WAITING 转为 RUNNABLE,进入异常处理逻辑。
例:
public class InterruptWaitingThreadDemo {
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 等待线程(通过wait()进入WAITING状态)
        Thread waitingThread = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    System.out.println(Thread.currentThread().getName() + ":进入WAITING状态");
                    LOCK.wait(); // 无时限等待
                } catch (InterruptedException e) {
                    System.out.println(Thread.currentThread().getName() + ":收到中断信号,被唤醒(抛出InterruptedException)");
                    System.out.println(Thread.currentThread().getName() + ":当前线程中断标志位:" + Thread.currentThread().isInterrupted()); // false,标志位已清除
                }
                System.out.println(Thread.currentThread().getName() + ":转为RUNNABLE状态,继续执行");
            }
        }, "等待线程");

        waitingThread.start();
        Thread.sleep(2000); // 确保线程进入WAITING状态

        // 中断线程:调用interrupt()唤醒等待线程
        Thread interruptThread = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + ":准备中断等待线程");
            waitingThread.interrupt();
            System.out.println(Thread.currentThread().getName() + ":中断操作完成");
        }, "中断线程");

        interruptThread.start();

        waitingThread.join();
        interruptThread.join();
        System.out.println("执行完成");
    }
}

运行结果

等待线程:进入WAITING状态
中断线程:准备中断等待线程
中断线程:中断操作完成
等待线程:收到中断信号,被唤醒(抛出InterruptedException)
等待线程:当前线程中断标志位:false
等待线程:转为RUNNABLE状态,继续执行
执行完成

注意事项

1. WAITING 与 TIMED_WAITING 的区别:
   TIMED_WAITING 是有时限等待(如 `wait(1000)`、`sleep(1000)`),
      超时后会自动转为 RUNNABLE;而 WAITING 无时限,
      必须依赖外部触发条件才能唤醒,二者唤醒逻辑不同。

2. 唤醒后并非立即执行:
   基于`Object.wait()`/`Condition.await()` 的唤醒,线程需重新竞争锁资源,
     只有竞争成功后才会真正转为 RUNNABLE 并执行;
     基于`LockSupport.unpark()` 的唤醒,
     线程会直接进入 RUNNABLE 队列,无需竞争锁。

3. `notify()` 与 `notifyAll()` 的选择:
   `notify()` 仅唤醒一个等待线程,
     可能存在唤醒“错误线程”的问题;
   `notifyAll()` 唤醒所有等待线程,虽会增加锁竞争,
     但能确保需要被唤醒的线程得到唤醒,
     推荐在不确定唤醒目标时使用 `notifyAll()`。

4. `LockSupport` 的许可机制:
   `LockSupport` 基于“许可”实现,
    每个线程最多持有一个许可,`unpark()` 会发放许可,
    `park()` 会消耗许可;若提前调用 `unpark()`,后续 `park()` 会直接返回,
     不会进入 WAITING 状态。

5. 中断唤醒会清除标志位:
   等待线程被中断唤醒后,`InterruptedException` 会清除中断标志位,
     若后续需要感知中断状态,
    需在异常块中调用 `Thread.currentThread().interrupt()` 重新设置标志位。
Java 线程 WAITING -> RUNNABLE 状态转换的核心触发条件可归纳为 5 类,核心要点如下:
  1.基础唤醒:`Object.wait()` → `Object.notify()`/`notifyAll()`(依赖 synchronized 锁);
  2.线程终止唤醒:`Thread.join()` → 目标线程执行完毕(自动唤醒);
  3.底层工具唤醒:`LockSupport.park()` → `LockSupport.unpark(Thread)`(无需锁,支持提前唤醒);
  4.条件唤醒:`Condition.await()` → `Condition.signal()`/`signalAll()`(依赖 Lock 锁,功能更灵活);
  5.中断唤醒:任意 WAITING 线程 → 被调用 `interrupt()`(抛出 InterruptedException,清除中断标志位)。
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaJingYan/202512/17666466948526.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者