Java 并发-线程状态

225 阅读3分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。

生命周期模型

线程的生命周期即线程在多种状态间转换的过程,可以用五种状态的模型来表示

image-20220120103933424

  1. 初始化状态

    线程在编程语言层面已被创建,而在操作系统层面还未创建真正的线程,如 Java 中还没有调用线程 start 方法

  2. 就绪状态

    操作系统层面的线程已经被创建,等待 CPU 调度执行

  3. 运行状态

    线程获取到 CPU 执行权限,开始执行线程的内容代码

  4. 休眠状态

    线程调用了阻塞式的 API 或者等待某个事件,就会进入休眠状态,释放 CPU 资源

    当等待的事件出现,线程回归就绪状态等待调度

  5. 死亡状态

    线程执行完毕,生命周期结束

Java 线程状态

  1. Java Thread 内部类 State 中,规定了 Java 线程的几种状态

    public enum State {
        // 初始化状态
        NEW,
        // 就绪 / 运行状态
        RUNNABLE,
        // 阻塞状态
        BLOCKED,
        // 无限等待
        WAITING,
        // 有时限等待
        TIMED_WAITING,
        // 死亡状态
        TERMINATED;
    }
    

    就绪状态和运行状态统一归为 RUNNABLE,而 BLOCKED、WAITING、TIMED_WAITING 都是休眠状态的一种

    需要注意的是调用阻塞式 API,如 IO 阻塞,在操作系统层面表现为休眠状态,但在 Java 虚拟机层面状态是 RUNNABLE

  2. NEW 与 RUNNABLE 的状态转换

    调用 start 方法

    Thread t = new Thread(() -> System.out.println(Thread.currentThread().getState()));
    System.out.println(t.getState());
    t.start();
    // NEW
    // RUNNABLE
    
  3. RUNNABLE 与 BLOCKED 的状态转换

    当执行中的线程等待 synchronized 锁时,状态变为 BLOCKED

    Object o = new Object();
    // 线程 1 获取锁睡 3 秒
    Thread t1 = new Thread(() -> {
        synchronized (o){
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    t1.start();
    Thread t2 = new Thread(() -> {
        synchronized (o){
            System.out.println("t2 run");
        }
    });
    t2.start();
    // 主线程睡 1 秒,用来打印线程 2 没有获取锁的状态
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println(t2.getState()); // BLOCKED
    
  4. RUNNABLE 与 WAITING 的状态转换

    • 获取 synchronized 锁的线程,调用了 wait 方法,notify 后变为 RUNNABLE

    • 线程 A 中调用了线程 B 的 join 方法,线程 A 变为 WAITING 状态,线程 B 执行完毕后线程 A 变为 RUNNABLE

    • LockSupport 的 park 方法是 JDK 并发包中实现锁的基础,会使线程变为 WAITING 状态,调用 unpark 方法后会变为 RUNNABLE

  5. RUNNABLE 与 TIMED_WAITING 的状态转换

    • 转换到 WAITING 的各种方式中,如果使用了超时参数,就是 TIMED_WAITING
    • 调用了带超时参数的 sleep 方法
  6. RUNNABLE 到 TERMINATED 的状态转换

    • run 方法执行完毕或抛出异常

    • 调用了中断线程的方法,如 stop(不推荐使用)

中断线程的方式

  1. 使用强制中断的 API

    stop、suspend 方法已经标记为 @Deprecated,不推荐使用

  2. 使用 interrupt 方法通知线程

    当线程内使用了 wait、join、sleep、lockInterruptibly(lock 锁) 这种可以抛出 InterruptedException 异常的 API,调用该线程的 interrupt 方法,就会触发线程内抛出 InterruptedException 异常

    也可以在线程内通过 isInterrupted 方法检查是否被中断,需要注意的是,在捕获异常后中断标记被自动清除,可以使用 Thread.currentThread().interrupt() 重新标记