并发编程学习(7) —— 线程的生命周期

通常的线程生命周期

在编程世界中,很多东西都有它的生命周期,都会经历“生老病死”,线程也是。
线程一共有以下几种状态:初始状态、可运行状态、运行状态、休眠状态、终止状态

  1. 初始状态:线程已创建但还不被允许分配CPU。在操作系统上线程还没有真正的创建。
  2. 可运行状态:线程可以分配CPU,同时意味着线程真正的创建成功。
  3. 运行状态:当有空闲的CPU时,操作系统会将其分配给一个可运行状态下的线程,获得CPU分配的线程会由可运行状态转为运行状态。
  4. 休眠状态:当线程执行到某个地方,需要等待或调用阻塞的API,就会释放CPU使用权,从运行状态转为休眠状态。休眠结束会转为可运行状态。
  5. 终止状态:当线程执行完或出现异常就会转为终止状态。终止状态的线程意味着生命周期结束。

具体可参考以下线程状态图:

并发编程学习(7) —— 线程的生命周期
线程状态图

JAVA中线程生命周期

与通用线程的生命周期不同,JAVA中的线程状态更加丰富一点,一共有以下六种状态:

  1. NEW(初始状态)
  2. RUNNABLE(可运行状态/运行状态)
  3. BLOCKED(阻塞状态)
  4. WAITING(无限等待)
  5. TIME_WAITING(有限时间等待)
  6. TERMINATED(终止状态)

可以看到,通用线程生命周期得可运行状态和运行状态合并成一个RUNNABLE状态,而BLOCK、WAITING和TIME_WAITING则属于休眠状态,当线程处于这三种状态之一就代表该线程没有CPU得使用权,可以参考下面得JAVA线程状态图:
并发编程学习(7) —— 线程的生命周期
那么这些状态是如何转换的呢?下面一一介绍:

RUNNABLE转BLOCK

这个比较好理解,一般只有等待synchronized隐式锁的时候才会由RUNNABLE转为BLOCK。

RUNNABLE转WAITING、TIME_WAITING

这里有三种情况:

  1. 调用Thread.join()方法。
  2. 调用Object.wait()方法。
  3. 调用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() 方法来判断。

结尾

线程的生命周期介绍就到这里。