并发编程学习(7) —— 线程的生命周期
通常的线程生命周期
在编程世界中,很多东西都有它的生命周期,都会经历“生老病死”,线程也是。
线程一共有以下几种状态:初始状态、可运行状态、运行状态、休眠状态、终止状态。
- 初始状态:线程已创建但还不被允许分配CPU。在操作系统上线程还没有真正的创建。
- 可运行状态:线程可以分配CPU,同时意味着线程真正的创建成功。
- 运行状态:当有空闲的CPU时,操作系统会将其分配给一个可运行状态下的线程,获得CPU分配的线程会由可运行状态转为运行状态。
- 休眠状态:当线程执行到某个地方,需要等待或调用阻塞的API,就会释放CPU使用权,从运行状态转为休眠状态。休眠结束会转为可运行状态。
- 终止状态:当线程执行完或出现异常就会转为终止状态。终止状态的线程意味着生命周期结束。
具体可参考以下线程状态图:
JAVA中线程生命周期
与通用线程的生命周期不同,JAVA中的线程状态更加丰富一点,一共有以下六种状态:
- NEW(初始状态)
- RUNNABLE(可运行状态/运行状态)
- BLOCKED(阻塞状态)
- WAITING(无限等待)
- TIME_WAITING(有限时间等待)
- TERMINATED(终止状态)
可以看到,通用线程生命周期得可运行状态和运行状态合并成一个RUNNABLE状态,而BLOCK、WAITING和TIME_WAITING则属于休眠状态,当线程处于这三种状态之一就代表该线程没有CPU得使用权,可以参考下面得JAVA线程状态图:
那么这些状态是如何转换的呢?下面一一介绍:
RUNNABLE转BLOCK
这个比较好理解,一般只有等待synchronized隐式锁的时候才会由RUNNABLE转为BLOCK。
RUNNABLE转WAITING、TIME_WAITING
这里有三种情况:
- 调用Thread.join()方法。
- 调用Object.wait()方法。
- 调用LockSupport.park()方法。
而TIME_WAITING和WAITING的区别主要是TIME_WAITING在触发条件上增加了超时参数。
NEW转RUNNABLE
创建Thread有以下两种方法:
第一种实现Runnable接口,重写run()方法
public class SimpleProiorities implements Runnable{
public void run() {
// 需要执行的代码
}
// 创建线程
Thread thread = new Thread(new SimpleProiorities());
}
第二种继承Thread,重写run()方法:
public class SimpleProiorities extends Thread{
public void run() {
// 需要执行的代码
}
// 创建线程
SimpleProiorities thread = new SimpleProiorities();
}
当创建后线程还不会执行,要想执行,就要调用以下方法:
// 创建线程
SimpleProiorities thread = new SimpleProiorities();
thread.start();
这时候就会从NEW状态转为RUNNABLE状态。
RUNNABLE转TERMINATED
有时候线程执行到一定程度,我们想强制终止该线程的执行,JDK提供stop()方法和interrupt()方法,但由于stop()方法过于粗暴,强制杀死线程,同时线程中的sychronized的隐式锁并不会释放,因此不推荐使用。
interrupt()方法则是通知该线程,并不会强制中断,线程可以选择继续执行,被通知的线程可以通过异常或主动检测来获得通知。
当线程处于WAITING、TIME_WAITING状态下时,若其它线程调用该线程的interrupt()方法,该线程就会把状态转为RUNNABLE,同时触发InterruptedException 异常。当线程处于RUNNABLE状态,若其它线程调用该线程的interrupt()方法,该线程就java.nio.channels.ClosedByInterruptException。
如果想知道该线程是否被中断,可以调用isInterrupted() 方法来判断。
结尾
线程的生命周期介绍就到这里。