3.4 线程的生命周期
线程的生命周期分为新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)这5种状态。在系统运行过程中不断有新的线程被创建,旧的线程在执行完毕后被清理,线程在排队获取共享资源或者锁时将被阻塞,因此运行中的线程会在就绪、阻塞、运行状态之间来回切换。线程的具体状态转化流程如图3-4所示。
图3-4
其流程如下所述。
(1)调用new方法新建一个线程,这时线程处于新建状态。
(2)调用start方法启动一个线程,这时线程处于就绪状态。
(3)处于就绪状态的线程等待线程获取CPU资源,在等待其获取CPU资源后线程会执行run方法进入运行状态。
(4)正在运行的线程在调用了yield方法或失去处理器资源时,会再次进入就绪状态。
(5)正在执行的线程在执行了sleep方法、I/O阻塞、等待同步锁、等待通知、调用suspend方法等操作后,会挂起并进入阻塞状态,进入Blocked池。
(6)阻塞状态的线程由于出现sleep时间已到、I/O方法返回、获得同步锁、收到通知、调用resume方法等情况,会再次进入就绪状态,等待CPU时间片的轮询。该线程在获取CPU资源后,会再次进入运行状态。
(7)处于运行状态的线程,在调用run方法或call方法正常执行完成、调用stop方法停止线程或者程序执行错误导致异常退出时,会进入死亡状态。
3.4.1 新建状态:New
在Java中使用new关键字创建一个线程,新创建的线程将处于新建状态。在创建线程时主要是为线程分配内存并初始化其成员变量的值。
3.4.2 就绪状态:Runnable
新建的线程对象在调用start方法之后将转为就绪状态。此时JVM完成了方法调用栈和程序计数器的创建,等待该线程的调度和运行。
3.4.3 运行状态:Running
就绪状态的线程在竞争到CPU的使用权并开始执行run方法的线程执行体时,会转为运行状态,处于运行状态的线程的主要任务就是执行run方法中的逻辑代码。
3.4.4 阻塞状态:Blocked
运行中的线程会主动或被动地放弃CPU的使用权并暂停运行,此时该线程将转为阻塞状态,直到再次进入可运行状态,才有机会再次竞争到CPU使用权并转为运行状态。阻塞的状态分为以下三种。
(1)等待阻塞:在运行状态的线程调用o.wait方法时,JVM会把该线程放入等待队列(Waitting Queue)中,线程转为阻塞状态。
(2)同步阻塞:在运行状态的线程尝试获取正在被其他线程占用的对象同步锁时,JVM会把该线程放入锁池(Lock Pool)中,此时线程转为阻塞状态。
(3)其他阻塞:运行状态的线程在执行Thread.sleep(long ms)、Thread.join()或者发出I/O请求时,JVM会把该线程转为阻塞状态。直到sleep()状态超时、Thread.join()等待线程终止或超时,或者I/O处理完毕,线程才重新转为可运行状态。
3.4.5 线程死亡:Dead
线程在以下面三种方式结束后转为死亡状态。
◎ 线程正常结束:run方法或call方法执行完成。
◎ 线程异常退出:运行中的线程抛出一个Error或未捕获的Exception,线程异常退出。
◎ 手动结束:调用线程对象的stop方法手动结束运行中的线程(该方式会瞬间释放线程占用的同步对象锁,导致锁混乱和死锁,不推荐使用)。