线程的生命周期详解

一、线程的生命周期

1、线程执行过程中的5中状态

(1)NEW
(2)RUNNABLE
(3)RUNNING
(4)BLOCKED
(5)TERMINATED

状态转换如下:

2、每种状态详解

(1)NEW状态

和使用关键字new创建一个对象相同,此时并不会执行,如果调用了start方法之后,线程将会进入到RUNNABLE状态;

(2)RUNNABLE状态

线程进入RUNNABLE状态必须调用start方法,此时JVM才会正式创建一个线程,但是这个时候的线程并不一定马上执行,需要等到CPU调度,获取到CPU的时间片之后,才会去执行。由于有RUNNING状态,所以线程不会直接进入BLOCKED状态和TERMINATED状态,即使调用了sleep和wait等阻塞方法。所以出于RUNNABLED状态的线程只能意外终止或者进入RUNNING状态。

(3)RUNNING状态

当一个线程获取到CPU的执行权之后,会去处理自己的业务逻辑,处理过程中,状态可能会发生如下的变化:

a. 进入TERMINATED状态,比如调用了stop方法或者判断满足了某个业务逻辑标识;
b. 进入BLOCKED状态,比如调用了wait方法或者sleep方法;
c. 进行了某个IO操作,比如网络调用或者数据库数据获取;
d. 获取某个锁资源,加入到该锁的阻塞队列中而进入了BLOCKED状态;
e. 主动调用了线程让步方法yield,放弃了CPU的执行权,进入了BLOCKED状态;
f. CPU调度过程中,放弃了对该线程的执行,使线程进入到了BLOCKED状态;

(4)BLOCKED状态

进入到BLOCKED状态的原因有好多种,如上述(3)中所述。进入到了BLOCKED状态之后,可以切换为以下几种状态:

a. 直接进入TERMINATED状态,比如调用了stop方法或者JVM进程崩溃;
b. 阻塞操作执行完毕,进入到了RUNNABLE状态,比如数据库数据读取完毕;
c. 完成了指定时间的休眠,比如sleep时间到了;
d. 处于wait中而被其他线程调用了notify或者notifyAll唤醒,进入到了RUNNABLE状态;
e. 线程获取到了某个锁资源,进入到了RUNNABLE状态;
f. 线程在阻塞过程中被打断,比如其他线程调用了interrupted方法,进入到了RUNNABLE状态;

(5)TERMINATED状态

线程如果进入到了TERMINATED状态之后,将不会再切换到其他任何状态。因为此时线程已经终止,导致线程进入TERMINATED状态的情况:

a. 线程正常执行结束,进入到了TERMINATED状态;
b. 线程运行过程中出现意外错误而结束,进入到了TERMINATED状态;
c. JVM进程崩溃,所有线程都意外结束;

二、线程的start方法

线程创建完成之后,调用了start方法,然后run方法中的内容就会执行,那么run方法是在哪调用的呢?
Thread中的start方法源码:

public synchronized void start() {
    if (threadStatus != 0)
        throw new IllegalThreadStateException();
    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 
            */
        }
     }
}

从上述代码中可以看出,调用start方法之后,JVM会先判断当前线程的状态是否为0,如果为0,则会调用start0这个方法,而start0这个方法为一个native方法,那么run方法在哪调用的呢?查看JDK的官方文档,会发现,run方法是在start0这个native方法中调用的,由JVM自身调用。
通过start方法的源码,会发现以下几点:

1、Thread被创建之后,其状态是由threadStatus这个标识控制;
2、同一个Thread对象,不能两次启动,否则会抛出IllegalThreadStateException异常;
3、线程结束之后,即处于TERMINATED状态的线程,如果再次调用start方法,也会抛出

IllegalThreadStateException异常;所以处于TERMINATED状态的线程是不能重新进入RUNNABLE和RUNNING状态的。
如下两种调用都会抛出上述异常:

(1)重复启动同一个Thread,会抛出IllegalThreadStateException异常

Thread t = new Thread() {
    @Override
    public void run() {
        // do something
    }
};
t.start(); // 启动正常
t.start(); // 再次启动,会抛出IllegalThreadStateException异常

(2)线程未结束之前,试图通过调用start方法,也会抛出IllegalThreadStateException异常

Thread t = new Thread() {
    @Override
    public void run() {
        // do something
    }
};
t.start(); // 启动正常
TimeUnit.MILLISECONDS.sleep(1000*10); // 等待线程生命周期结束
t.start(); // 试图再次调用start,会抛出IllegalThreadStateException异常

结论:上述两种方法虽然都会抛出IllegalThreadStateException异常,但是第一种是由于重复启动导致的,此时线程生命周期还未结束。但是第二种是因为线程的生命周期已经结束,此时调用start方法,相当于已经没有线程了,所以也没有线程状态,抛出异常。

注:文章属原创,如果转发,请标注出处。