“曾经的红衣少年,如今的白发先生",这是给季羡林的感动中国颁奖词中的一段,从呱呱坠地到耄耋之年,每个人都有其生命周期。Java中的线程亦不例外,也有自己的生命周期。Java中的生命周期状态由Thread.State这个枚举类定义,一共定义了6中状态。分别是:
- NEW->新建
- RUNNABLE->可运行状态
- BLOCKED->阻塞状态
- WAITING->等待
- TIMED_WAITING->记时等待
- TERMINATED->终结
可以通过thread.getState()方法获取线程的状态,且任意时刻线程只可能处于一种状态(毕竟标识状态就只有一个变量)。
如图所示是Java线程状态之间的流转关系:
NEW-新创建
NEW表示线程创建但尚未启动的状态:当我们执行new Thread()新建一个线程时,且线程没有调用 start()方法,那么此时线程的状态就是NEW。而一旦调用了start()方法,线程的状态就会变为RUNNABLE
RUNNABLE-可运行
Java线程的RUNNABLE状态其实应该对应的是ready和running状态。当前的操作系统都是分时多道系统,线程或进程都是被cpu调度分配其配额的时间分片执行,当用完时间分片,就会放回调度队列等待下一次调用度分配。所以线程调用strat()后,只是告诉操作系统我准备好被调度了,可以为我分配时间片了,但是是不是能立马分到时间片,这个是要看操作系统如何调度的。当一个线程当前的时间分片用完了但当前线程的任务并未执行完,CPU被调度到执行其他任务,导致该线程暂时不运行,它的状态依然不变,还是 RUNNABLE,因为它有可能随时被调度回来继续执行任务。也就是说Java中处于RUNNABLE状态的线程,可能正在运行,也可能正在等待分配CPU资源。
BLOCKED-阻塞状态
从 RUNNABLE状态进入 BLOCKED状态只有一种可能,就是进入 synchronized 保护的代码时没有抢到 monitor 锁,无论是进入 synchronized 代码块,还是 synchronized 方法,都是一样。而当处于 BLOCKED的线程抢到 monitor 锁,就会从 BLOCKED 状态回到RUNNABLE 状态
WAITING-等待
线程进入WAITING状态有三种可能性。
- 没有设置 Timeout 参数的 Object.wait() 方法。
- 没有设置 Timeout 参数的 Thread.join() 方法。
- LockSupport.park() 方法。
BLOCKED 仅仅针对 synchronized monitorjvm层面实现的,可是在 Java 中还有很多其他的锁java语言层面实现的,比如 ReentrantLock,如果线程在获取这种锁时没有抢到该锁就会进入 WAITING 状态,因为本质上它执行了 LockSupport.park() 方法,所以会进入 WAITING 状态。同样,Object.wait() 和 Thread.join() 也会让线程进入 Waiting 状态。
BLOCKED 与 WAITING 的区别是 Blocked 在等待其他线程释放 monitor 锁,而 WAITING 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll() 。
TIMED_WAITING-计时等待
WAITING和TIMED_WAITING这两个状态是非常相似的,区别仅在于有没有时间限制,TIMED_WAITING会等待超时,由系统自动唤醒,或者在超时前被唤醒信号唤醒。
以下情况会让线程进入TIMED_WAITING状态。
- 设置了时间参数的
Thread.sleep(long millis)方法; - 设置了时间参数的
Object.wait(long timeout)方法; - 设置了时间参数的
Thread.join(long millis)方法; - 设置了时间参数的
LockSupport.parkNanos(long nanos)方法和LockSupport.parkUntil(long deadline)方法。
Thread.sleep(times)超时结束或者被中断后线程状态会由TIMED_WAITING变为RUNNABLE。
LockSupport.parkNanos(long nanos)/ LockSupport.parkUntil(long deadline) 超时结束后线程状态会由TIMED_WAITING变为RUNNABLE。
Object.wait(long timeout)超时结束,或者被其他持有monitor的线程调用了notify()/notifyAll()线程状态会由TIMED_WAITING变BLOCKED,因为此时别的线程可能持有了monitor,同一时刻只能有一个线程持有monitor
Thread.join(long millis)超时结束后线程状态会由TIMED_WAITING变为RUNNABLE。
TERMINATED-终止
TERMINATED终止状态,要想进入这个状态有两种可能。
- run() 方法执行完毕,线程正常退出。
- 出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。
Java中线程的状态需要严格按图示箭头方向走,线程的生命周期不可逆,进入了RUNNABLE状态将无法恢复到NEW状态,而一旦被终止将不能再有任何状态的变更。故线程只能有一次NEW和TERMINATED状态,只有处于中间的状态才能相互转换。
参考资料:
《Java并发编程的艺术》--方腾飞
《Thread.java》 ---------JDK8