Java线程的生命周期

时间:2024-06-27 23:35:26

 线程的生命周期包括:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。线程状态转换图如下:

Java线程的生命周期

 1、新建状态(New)

 当程序使用new关键字创建一个线程后,Java虚拟机为其分配内存,并初始化其成员变量的值,此时处于新建状态,程序不会执行线程的线程执行体run()。

 2、就绪状态(Runnable)

当线程对象调用了start()方法后,该线程就处于就绪状态,此时Java虚拟机会为其创建方法调用栈和程序计数器,处于此状态的线程只是表示该线程可以运行了,至于何时开始运行,则取决与JVM里线程调度器的调度。线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。

 注意:不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadStateException异常。如果希望子线程调用start()方法后立即执行,可以使用Thread.sleep(1)方式使主线程睡眠1毫秒,转去执行子线程,因为这1毫秒内CPU不会空闲,它会去执行另一个就绪的线程。

 3、运行状态(Running)

 线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。对于抢占式策略的系统而言,系统会给每个线程一个小时间段来处理任务,当该时间段用完后,系统会剥夺该线程占用的资源,让其他线程获得执行的机会(考虑优先级)。

 如果该线程失去了cpu资源,就会又从运行状态变为就绪状态。重新等待系统分配资源。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。

当发生如下情况是,线程会从运行状态变为阻塞状态:

①、线程调用sleep()方法主动放弃所占用的系统资源。

②、线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞。

③、线程试图获得一个同步锁,但该资源被其他线程占有。

④、线程在等待某个通知(notify)。

⑤、程序调用了线程的suspend()方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法。

当线程的run()/call()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()方法等等,就会从运行状态转变为死亡状态。

 4、阻塞状态(Running)

 由于某种原因导致正在运行的线程让出CPU资源并暂停自己的执行,即进入堵塞状态。如上面讲到的5点都能将运行状态的线程转换为阻塞状态。在阻塞状态的线程不能进入就绪队列,只有当引起阻塞的原因解除后,线程便转入就绪状态,重新到就绪队列中排队,等待线程调度器再次调度。解除阻塞的几种情况:

①、调用sleep()方法线程经过了指定时间。

②、调用的阻塞式IO方法已经返回。

③、线程成功获得申请的同步锁。

④、线程在等待某个通知(notify)时,其他线程发出了一个通知。

⑤、处于挂起状态的线程被调用了resume()恢复方法。

 5、死亡状态(Dead)

 线程会以如下3种方式结束,结束后就处于死亡状态。

 ①、run()或call()方法执行完成,线程正常结束。

②、线程抛出一个未捕获的Exception或Error。

③、直接调用线程的stop()方法来结束线程——该方法容易导致死锁,通常不推荐使用。

 注意:可调用线程对象的isAlive()方法,测试线程是否死亡(线程处于就绪、运行、阻塞3种状态时返回true)。同理,不能对已经死亡的线程再次调用start()方法,否则会出现java.lang.IllegalThreadStateException异常。

 关于yeild()方法的使用,请看下一篇《线程的控制》。