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

- 初始状态:指的是线程已经被创建但还不允许分配CPU资源。这个状态是编程语言特有的,而且这里的“创建”也是指编程语言层面的被创建,实际上并没有在操作系统层面上创建线程。
- 可运行状态:在这个状态下线程可以分配CPU资源,而且真正的操作系统级别的线程已经被创建,线程一旦分配到CPU资源就会立即运行。
- 运行状态:当有空闲CPU资源时,操作系统会挑选一个处于可运行状态的线程并为其分配CPU资源,被挑选出来分配到CPU资源的线程就会进入运行状态。
- 休眠状态:当运行状态的线程调用一个阻塞API或者等待某个事件时,那么它的状态就会从运行状态转换为休眠状态。处于休眠状态的线程永远不会分配到CPU资源。当等待的事件出现时,休眠状态的线程就会转换到可运行状态。
- 终止状态:线程运行完成或者出现异常时就会进入终止状态,终止状态的线程不会再转变成其他状态。线程进入终止状态意味着生命周期的结束。
Java 中线程的声明周期
Java 中的线程一共有6个状态:
- NEW(新建)
- RUNNABLE(可运行/运行)
- BLOCKED(阻塞)
- WAITING(无限时等待)
- TIMED_WAITING(有限时等待)
- TERMINATED(终止)
看上去很复杂,但其实 blocked、waiting、timed_waiting 这三种状态对应的都是操作系统中的同一种状态 ---- 休眠状态。
所以,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 状态。
网友评论