Java 线程的状态及状态转换

通用的线程周期

操作系统中,线程的状态一般包含以下五种:初始状态可运行状态运行状态休眠状态终止状态

通用的线程状态转换图--五态模型

  1. 初始状态:指的是线程已经被创建但还不允许分配CPU资源。这个状态是编程语言特有的,而且这里的“创建”也是指编程语言层面的被创建,实际上并没有在操作系统层面上创建线程。
  2. 可运行状态:在这个状态下线程可以分配CPU资源,而且真正的操作系统级别的线程已经被创建,线程一旦分配到CPU资源就会立即运行。
  3. 运行状态:当有空闲CPU资源时,操作系统会挑选一个处于可运行状态的线程并为其分配CPU资源,被挑选出来分配到CPU资源的线程就会进入运行状态。
  4. 休眠状态:当运行状态的线程调用一个阻塞API或者等待某个事件时,那么它的状态就会从运行状态转换为休眠状态。处于休眠状态的线程永远不会分配到CPU资源。当等待的事件出现时,休眠状态的线程就会转换到可运行状态。
  5. 终止状态:线程运行完成或者出现异常时就会进入终止状态,终止状态的线程不会再转变成其他状态。线程进入终止状态意味着生命周期的结束。

Java 中线程的生命周期

Java 中的线程一共有6个状态:

  1. NEW(新建)
  2. RUNNABLE(可运行/运行)
  3. BLOCKED(阻塞)
  4. WAITING(无限时等待)
  5. TIMED_WAITING(有限时等待)
  6. TERMINATED(终止)

看上去很复杂,但其实 blocked、waiting、timed_waiting 这三种状态对应的都是操作系统中的同一种状态 ---- 休眠状态。
所以,Java 线程的声明周期可以简化为下图:


Java 线程的状态转换图

它们之间的转换关系是什么样的呢,我们来分析一下:

1.NEW → RUNNABLE

NEW 状态在 Java 语言中对应于调用start()方法之前的Thread的实例。所以从 NEW 到 RUNNABLE 的转换很简单就是调用一下start()方法。

2.RUNNABLE ⇌ BLOCKED

只有一种情况会让线程从RUNNABLE 状态转换为 BLOCKED 状态,就是线程等待 synchronized 隐式锁。synchronized 关键字修饰的代码块、方法在同一时刻只允许一个线程执行,其他线程只能等待,等待的线程就会从 RUNNABLE 状态转换成 BLOCKED 状态。而当线程获取到隐式锁时就会从 BLOCKED 状态转换成 RUNNABLE 状态。
应当注意的是:当线程调用阻塞API时,在操作系统层面上看,线程会进入休眠态,但是在Java层面上看,此时这个Java线程依然是RUNNABLE 状态。

3.RUNNABLE ⇌ WAITING

有三种情况会触发 RUNNABLE 和 WAITING 之间的转换:

  • 场景一:在 synchronized 代码块中调用 object.wait() 方法
  • 场景二:处于 RUNNABLE 状态的线程调用thread.join()方法等待某个线程运行完成。例如thread1中有一行代码是thread2.join()则执行这行代码后thread1会从RUNNABLE 状态转换成 WAITING 状态,直到thread2执行完成以后,thread1才会从 WAITING 状态再次回到 RUNNABLE 状态。
  • 场景三:调用LockSupport.park()方法,会让当前线程从RUNNABLE 转换为 WAITING。当某个线程调用了LockSupport.unpark(thread)时,thread方法就会从WAITING状态转换成RUNNABLE状态。
4.RUNNABLE ⇌ TIMED_WAITING

有四种场景可以使得线程从 RUNNABLE 状态转换到 TIMED_WAITING 状态:

  • 场景一:Object.wait(long timeout)
  • 场景二:Thread.join(long timeout)
  • 场景三:LockSupport.parkUntil(long deadline) (还有一个park型方法,这里不列举了)
  • 场景四:Thread.sleep(long timeout)

可以看出,这四种场景的前三种都是上面提到的函数的带时间参数的形式,最后一个是我们最直接可以想到的sleep。

5.RUNNABLE → TERMINATED

线程执行完 run() 方法后就会自动进入 TERMINATED 状态,如果抛出异常的话也会进入到这个状态。有时候,我们需要强行停止 run() 方法的运行,这时候我们只需要调用线程的 interrupt() 方法即可让线程直接进入 TERMINATED 状态。
应当注意的是,Java 还提供了Thread.stop()方法强制停止线程,但这个方法非常残暴,它使得线程直接停止而无法执行后续必要操作,比如无法释放已经获取的锁。而interrupt就比较温和,它只是通知一下线程应当停止了,而线程什么时候停止则取决于线程本身。如果线程处于WAITING、TIMED_WAITING时,那么interrupt则会唤醒线程,让其重新进入 RUNNABLE 状态然后抛出 InterruptedException 异常。如果线程处于 RUNNABLE 状态,则线程会在合适的时候检测一下自己是否被需要中断,如果需要中断则首先需要做一些必要的操作,然后再进入 TERMINATED 状态。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。