线程生命周期
现代操作系统在运行一个程序时,会为其创建一个进程。例如,启动一个Java程序,操作系统就会创建一个Java进程。现代操作系统调度的最小单元是线程,也叫轻量级进程(Light Weight Process),在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。
CPU再切换线程是会导致线程存在各种状态,线程从创建到死亡其中存在不同的生命状态;本文将对线程生命周期进行全面的介绍。
Java线程的状态可以从java.lang.Thread
的内部枚举类java.lang.Thread$State
得知:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked、WATING、TIMED_WATING)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是 线程状态也会多次在运行、阻塞之间切换。
-
新建状态(New)
API注释:
/**
* Thread state for a thread which has not yet started.
* 线程实例尚未启动时候的线程状态
*/
NEW,当线程对象创建后,即进入新建状态,如:
Thread t = new MyThread();
-
就绪状态(Runnable)
API注释:
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE, 可运行状态下线程的线程状态。可运行状态下的线程在Java虚拟机中执行,但它可能执行等待操作系统的其他资源,例如处理器。当调用线程对象的
start()
方法时,线程即进入就绪状态。处于就绪状态的线程只是说明此线程已经做好准备,随时等待CPU调度执行,并不是说执行了start()
方法就立即执行。当Java线程实例调用了
Thread#start()
之后,就会进入RUNNABLE
状态。RUNNABLE
状态可以认为包含两个子状态:READY
和RUNNING
。-
READY
:该状态的线程可以被线程调度器进行调度使之更变为RUNNING
状态。 -
RUNNING
:该状态表示线程正在运行,线程对象的run()
方法中的代码所对应的的指令正在被CPU执行。 - 当Java线程实例
Thread#yield()
方法被调用时或者由于线程调度器的调度,线程实例的状态有可能由RUNNING
转变为READY
,但是从线程状态Thread#getState()
获取到的状态依然是RUNNABLE
。例如: public class ThreadState1 { public static void main(String[] args) throws Exception {
Thread thread = new Thread(()-> {
while (true){
Thread.yield();
}
});
thread.start();
Thread.sleep(2000);
System.out.println(thread.getState());
}
}
// 输出结果
RUNNABLE
-
-
运行状态(Running)
当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。
- WATING状态
WAITING
是无限期的等待状态,这种状态下的线程不会被分配CPU执行时间。当一个线程执行了某些方法之后就会进入无限期等待状态,直到被显式唤醒,被唤醒后,线程状态由WAITING
更变为RUNNABLE
然后继续执行。
- TIMED WATING状态
TIMED WAITING
就是有限期等待状态,它和WAITING
有点相似,这种状态下的线程不会被分配CPU执行时间,不过这种状态下的线程不需要被显式唤醒,只需要等待超时限期到达就会被VM唤醒,有点类似于现实生活中的闹钟。
- 阻塞状态(Blocked)
处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行,此时进入阻塞状态,直到其进入到就绪状态,才有机会再次被CPU调用以进入到运行状态。
阻塞状态分类
- 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
- 同步阻塞:线程在获取
synchronized
同步锁失败(因为锁被其它线程占用),它会进入到同步阻塞状态;- 其他阻塞:通过调用线程的sleep()或join()或发出I/O请求时,线程会进入到阻塞状态。当
sleep()
状态超时、join()
等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
-
死亡状态
线程执行完毕或者是异常退出,该线程结束生命周期。
线程相关方法
public class Thread{
// 线程的启动
public void start();
// 线程体
public void run();
// 已废弃
public void stop();
// 已废弃
public void resume();
// 已废弃
public void suspend();
// 在指定的毫秒数内让当前正在执行的线程休眠
public static void sleep(long millis);
// 同上,增加了纳秒参数
public static void sleep(long millis, int nanos);
// 测试线程是否处于活动状态
public boolean isAlive();
// 中断线程 - 进入阻塞状态
public void interrupt();
// 测试线程是否已经中断
public boolean isInterrupted();
// 测试当前线程是否已经中断
public static boolean interrupted();
// 等待该线程终止
public void join() throws InterruptedException;
// 等待该线程终止的时间最长为 millis 毫秒
public void join(long millis) throws InterruptedException;
// 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒
public void join(long millis, int nanos) throws InterruptedException;
}
笔者的微信公众号,每天一篇好文章: