开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 18 天,点击查看活动详情
前言
Java 中的线程生命周期总共可以分为六种状态:
- 初始化状态 (NEW)
- 可运行/运行状态 (RUNNABLE)
- 阻塞状态 (BLOCKED)
- 无时限等待状态 (WAITING)
- 有时限等待状态 (TIMED_WAITING)
- 终止状态 (TERMINATED)
虽然 Java 语言中线程的状态比较多,但在操作系统层面,Java线程中的阻塞状态、无时限等待状态、有时限等待状态都是一种状态,即通用线程生命周期中的休眠状态。
Java 中的各种状态中可以有如下的转化:
RUNNABLE 与 BLOCKED 的状态转换
只有一种场景会触发这种转换,就是线程等待 synchronized 锁,synchronized 修饰的方法、代码块同一时刻只允许一个线程执行,其他的线程则需要等待。此时,等待的线程就会从 RUNNABLE 状态转换到 BLOCKED 状态。当等待的线程获得 synchronized 锁时,就又会从 BLOCKED 状态转换到 RUNNABLE 状态。
线程调用阻塞 API 时,在操作系统层面,线程会转换到休眠状态。但是在JVM中,Java线程态仍然是 RUNNABLE 状态。JVM 并不关心操作系统调度相关的状态,在 JVM 角度来看,等待CPU使用权 (操作系统中的线程处于可执行状态) 和等待IO操作 (操作系统中的线程处于休眠状态) 没有区别,都是在等待某个资源。
RUNNABLE 与 WAITING 状态转换
总体上有三种情况:
- 获得 synchronized 锁的线程,调用无参的 Object.wait()方法。此时的线程会从 RUNNABLE 状态转换成 WAITING 状态。
- 调用无参数的 Thread.join() 方法。例如:在 threadA 线程中调用 threadB 线程的 join() 方法,则threadA 线程会等待 threadB 线程执行完。而 threadA 线程在等待 threadB 线程执行的过程中,其状态会从 RUNNABLE 转换到 WAITING。当 threadB 执行完毕,threadA 线程的状态则会从 WAITING 状态转换成 RUNNABLE 状态。
- 调用 LockSupport.park() 方法,当前线程会阻塞,线程的状态会从 RUNNABLE 转换成 WAITING;调用 LockSupport.unpark(Thread thread) 可唤醒目标线程,目标线程的状态又会从 WAITING 状态转换到 RUNNABLE。
RUNNABLE 与 TIMED_WAITING 状态转换
总体上有五种情况:
- 调用带超时参数的Thread.sleep(long millis)方法
- 获得 synchronized 锁的线程,调用带超时参数的 Object.wait(long timeout) 参数
- 调用带超时参数的 Thread.join(long millis) 方法
- 调用带超时参数的 LockSupport.parkNanos(Object blocker, long deadline) 方法
- 调用带超时参数的 LockSuppor.parkUntil(long deadline) 方法
从 NEW 到 RUNNABLE 状态
Java 刚创建出来的 Thread 对象就是 NEW 状态;Java 中的线程要执行,需要转换到 RUNNABLE 状态。从 NEW 状态转换到 RUNNABLE 状态,只需要调用线程对象的 start() 方法即可。