在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)或者结束(Terminated) 5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是 线程状态也会多次在运行、阻塞之间切换。
New
Thread thread = new Thread(() -> System.out.println("测试线程"));
上面只是创建一个Thread类的实例对象,开没有开启线程也不会直接进入Runnable
状态,而是在调用start()
方法后才会
Runnable
当我们在代码中显式的调用 start() 方法后,JVM 进程会去创建一个新的线程,而此线程不会马上被 CPU 调度运行,进入 RUNNING 状态,这里会有一个中间状态,就是 RUNNABLE 状态,可以理解为等待被 CPU 调度的状态,是否执行要看是否获得CPU的执行权
在RUNNABLE 状态的线程无法直接进入BLOCKED 状态和 TERMINATED 状态,只有获得 CPU 调度执行权的线程才有资格进入 BLOCKED 状态和 TERMINATED 状态
Running
当RUNNABLE 状态的线程获得CPU执行权之后进入RUNNING 状态,开始执行线程包括 run方法。
RUNNING 状态的线程可能发生的状态转换:
- 被转换成 TERMINATED 状态,比如调用 stop() 方法;
- 被转换成 BLOCKED 状态,当线程调用了自身的sleep()方法或其他线程的join()方法就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源,不释放”锁标志”)
- 被转换成 BLOCKED 状态,如进行 IO 阻塞操作,如查询数据库进入阻塞状态;
- 被转换成 BLOCKED 状态,比如获取某个锁的释放,而被加入该锁的阻塞队列中;
- 该线程的时间片用完,CPU 再次调度,进入 RUNNABLE 状态;
- 线程主动调用 yield 方法,让出 CPU 资源,进入 RUNNABLE 状态;
Blocked
进入Blocked状态的线程可能的转变:
- 被转换成 TERMINATED 状态,比如调用 stop() 方法,或者是 JVM 意外 Crash;
- 被转换成 RUNNABLE 状态,阻塞时间结束,比如读取到了数据库的数据后;
- 完成了指定时间的休眠,进入到 RUNNABLE 状态;
- 正在 wait 中的线程,被其他线程调用 notify/notifyAll 方法唤醒,进入到 RUNNABLE 状态;
- 线程获取到了想要的锁资源,进入 RUNNABLE 状态;
- 线程在阻塞状态下被打断,如其他线程调用了 interrupt 方法,进入到 RUNNABLE 状态;
(锁池状态)
当线程刚进入就绪状态(注意,还没运行),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入就绪状态,等待OS分配CPU时间片
(队列状态)
当线程调用wait()方法后,会释放掉它所占有的“锁标志”并进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。
阻塞的情况分三种:
(1)、等待阻塞(对应队列状态):运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒
(2)、同步阻塞(对应锁池状态):运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
(3)、其他阻塞(对应阻塞状态):运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
Dead
Dead状态是线程的最终状态,处于此状态中的线程不会切换到以上任何状态,一旦线程进入了 Dead状态,就意味着这个线程生命的终结,以下情况下,线程会进入到 TERMINATED 状态:
- 线程正常运行结束,生命周期结束;
- 线程运行过程中出现意外错误;
- JVM 异常结束,所有的线程生命周期均被结束。
start()和run()方法的比较:
start()方法的源码,底层调用了start0()方法,开启线程执行线程方法
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
执行run()则会随着主线程一起执行,并不会开启一个线程