一、线程的生命周期
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方法,相当于已经没有线程了,所以也没有线程状态,抛出异常。
注:文章属原创,如果转发,请标注出处。