这是我参与更文挑战的第4天,活动详情查看:更文挑战
本文已参与周末学习计划,点击查看详情。
Java定义了6种线程状态,在任意时间点,每一个线程只能是其中的一种状态;并且对于单核CPU,任意时间点,只能存在一个线程为运行状态。线程状态通过特定的机制和方法进行转换。
1 Java线程的6种状态
线程状态的本质是 Thread 类中的一个变量,叫 private volatile int threadStatus = 0;
。而这个变量的值通过 Thread 内部的一个枚举类定义。
public enum State {
/**
* 新建状态
* 创建后尚未启动的线程
*/
NEW,
/**
* 运行状态
* 包括正在执行,也可能正在等待操作系统为它分配执行时间
*/
RUNNABLE,
/**
* 阻塞状态
* 一个线程因为等待临界区的锁被阻塞产生的状态
*/
BLOCKED,
/**
* 无限期等待状态
* 线程不会被分配处理器执行时间,需要等待其他线程显式唤醒
*/
WAITING,
/**
* 限期等待状态
* 线程不会被分配处理器执行时间,但也无需等待被其他线程显式唤醒
* 在一定时间之后,它们会由操作系统自动唤醒
*/
TIMED_WAITING,
/**
* 结束状态
* 线程退出或已经执行完成
*/
TERMINATED;
}
复制代码
2 无中断的线程状态转换
正常一个线程,最简单的生命周期都应该经历这三个过程:**初始 –> 运行 –> 终止 **。
初始(NEW)
- 当一个线程对象被创建出来时,线程并没有立即启动,而是将threadStatus变量初始化为 NEW 状态。
运行(RUNNABLE)
- 我们都知道,启动一个线程的方法是调用Thread的
start()
方法- 调用
start()
方法后,最终会调用一个本地方法private native void start0();
- 在JVM中,
start0()
方法最终会调用到操作系统创建线程的方法- 在调用
Thread.start()
之后,线程的状态将会变为 RUNNABLE 状态- 在单核CPU中,同一时刻,只能运行一个线程,具体启动哪个线程,由操作系统调度。在JVM层面,虽然线程为 RUNNABLE 状态,但不代表就是运行中,只能说得到了随时运行的机会。而在操作系统层面看,处于可运行状态的线程都会在一个就绪队列中等待调度,最终进入CPU运行的才是真正的RUNNING,仍处于就绪队列中的都叫READY。
- Java没有区分RUNNING 和 READY,将这两种都叫RUNNABLE
终止(TERMINATED)
- 当一个线程执行完毕,线程的状态就变为 TERMINATED。
- 处于 TERMINATED 状态的线程都无法死灰复燃,如果强行再启动线程,将会抛出
java.lang.IllegalThreadStateException
异常。
3 线程阻塞
当两个并发线程访问同一个对象的synchronized
代码块时,获得锁的线程会进入RUNNABLE 状态;而另一个线程将进入 BLOCKED 状态,知道下一次获得对象锁才能再一次进入 RUNNABLE 状态。
当多个并发线程争抢同一个对象锁时,获得锁的线程会进入RUNNABLE 状态;其他所有对象都将进入 BLOCKED 状态,并进入锁对象的同步队列中;当持有锁的这个线程,释放了锁之后,会唤醒该锁对象同步队列中的所有线程,这些线程会继续尝试抢锁。如此往复。
4 wait() 、 join() 和 park()
当使用 wait()
、join()
和 park()
这三个方法时,都会使线程进入WAITING 状态。这三个方法的使用会有一定的区别。
wait()
- wait() 方法是运行中的线程主动调用的
- 当运行中的线程调用wait() 之后,会释放锁对象,线程状态变成WAITING,线程进入锁对象的等待对列
- 当另一个获得锁对象的线程调用 notify/notifyAll 方法时,等待队列 中的线程才会被唤醒
- 被唤醒的线程争抢对象锁,获得锁的进入RUNNABLE状态,没有获得锁的进入BLOCKED 状态,并进入锁对象的同步队列中
join()
- 当其他线程调用 join() 方法时,正在运行的线程会被迫退出 RUNNABLE 状态,进入 WAITING
- join() 底层仍然是通过调用wait(),由正在运行的线程调用wait() 方法,锁对象是线程本身
- 使用join() 方法插队后进入运行状态的线程,在执行完毕会自动调用notifyAll() 方法
park()
- 一个线程调用
LockSupport.park()
方法,该线程状态会从 RUNNABLE 变成 WAITING- 另一个线程调用
LockSupport.unpark(Thread 刚刚的线程)
,刚刚的线程会从 WAITING 回到 RUNNABLE
5.TIMED_WAITING
这部分就再简单不过了,将上面导致线程变成 WAITING 状态的那些方法,都增加一个超时参数,就变成了将线程变成 TIMED_WAITING 状态的方法了。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END