Java多线程学习(三)---线程的生命周期

线程生命周期

摘要服务器

当线程被建立并启动之后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要通过新建(New)、就绪Runnable)、运行Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤为是当线程启动之后,它不可能一直"霸占"着CPU独自运行,因此CPU须要在多条线程之间切换,因而线程状态也会屡次在运行、阻塞之间切换并发

1. 新建状态,当程序使用new关键字建立了一个线程以后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值测试

2. 就绪状态,当线程对象调用了start()方法以后,该线程处于就绪状态。Java虚拟机会为其建立方法调用栈和程序计数器,等待调度运行spa

3. 运行状态,若是处于就绪状态的线程得到了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态操作系统

4. 阻塞状态,当处于运行状态的线程失去所占用资源以后,便进入阻塞状态线程

5. 在线程的生命周期当中,线程的各类状态的转换过程设计

1、新建和就绪状态

当程序使用new关键字建立了一个线程以后,该线程就处于新建状态,此时它和其余的Java对象同样,仅仅由Java虚拟机为其分配内存,并初始化其成员变量的值。此时的线程对象没有表现出任何线程的动态特征,程序也不会执行线程的线程执行体。对象

当线程对象调用了start()方法以后,该线程处于就绪状态。Java虚拟机会为其建立方法调用栈程序计数器,处于这个状态中的线程并无开始运行,只是表示该线程能够运行了。至于该线程什么时候开始运行,取决于JVM里线程调度器的调度。blog

注意:启动线程使用start()方法,而不是run()方法。永远不要调用线程对象的run()方法。调用start0方法来启动线程,系统会把该run()方法当成线程执行体来处理;但若是直按调用线程对象的run()方法,则run()方法当即就会被执行,并且在run()方法返回以前其余线程没法并发执行。也就是说,系统把线程对象当成一个普通对象,而run()方法也是一个普通方法,而不是线程执行体。须要指出的是,调用了线程的run()方法以后,该线程已经再也不处于新建状态,不要再次调用线程对象的start()方法。只能对处于新建状态的线程调用start()方法,不然将引起IllegaIThreadStateExccption异常。

调用线程对象的start()方法以后,该线程当即进入就绪状态——就绪状态至关于"等待执行",但该线程并未真正进入运行状态。若是但愿调用子线程的start()方法后子线程当即开始执行,程序可使用Thread.sleep(1) 来让当前运行的线程(主线程)睡眠1毫秒,1毫秒就够了,由于在这1毫秒内CPU不会空闲,它会去执行另外一个处于就绪状态的线程,这样就可让子线程当即开始执行。生命周期

2、运行和阻塞状态

2.1 线程调度

若是处于就绪状态的线程得到了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态,若是计算机只有一个CPU。那么在任什么时候刻只有一个线程处于运行状态,固然在一个多处理器的机器上,将会有多个线程并行执行;当线程数大于处理器数时,依然会存在多个线程在同一个CPU上轮换的现象。

当一个线程开始运行后,它不可能一直处于运行状态(除非它的线程执行体足够短,瞬间就执行结束了)。线程在运行过程当中须要被中断,目的是使其余线程得到执行的机会,线程调度的细节取决于底层平台所采用的策略。对于采用抢占式策略的系统而言,系统会给每一个可执行的线程一个小时间段来处理任务;当该时间段用完后,系统就会剥夺该线程所占用的资源,让其余线程得到执行的机会。在选择下一个线程时,系统会考虑线程的优先级

全部现代的桌面和服务器操做系统都采用抢占式调度策略,但一些小型设备如手机则可能采用协做式调度策略,在这样的系统中,只有当一个线程调用了它的sleep()或yield()方法后才会放弃所占用的资源——也就是必须由该线程主动放弃所占用的资源。

2.2 线程阻塞

当发生以下状况时,线程将会进入阻塞状态

线程调用sleep()方法主动放弃所占用的处理器资源

线程调用了一个阻塞式IO方法,在该方法返回以前,该线程被阻塞

线程试图得到一个同步监视器,但该同步监视器正被其余线程所持有。关于同步监视器的知识、后面将存更深刻的介绍

线程在等待某个通知notify

程序调用了线程的suspend()方法将该线程挂起。但这个方法容易致使死锁,因此应该尽可能避免使用该方法

当前正在执行的线程被阻塞以后,其余线程就能够得到执行的机会。被阻塞的线程会在合适的时候从新进入就绪状态,注意是就绪状态而不是运行状态。也就是说,被阻塞线程的阻塞解除后,必须从新等待线程调度器再次调度它。

2.3 解除阻塞

针对上面几种状况,当发生以下特定的状况时能够解除上面的阻塞,让该线程从新进入就绪状态

调用sleep()方法的线程通过了指定时间。

线程调用的阻塞式IO方法已经返回。

线程成功地得到了试图取得的同步监视器。

线程正在等待某个通知时,其余线程发出了个通知。

处于挂起状态的线程被调甩了resdme()恢复方法。

图 2.1 线程状态转换图

从图2.1中能够看出,线程从阻塞状态只能进入就绪状态,没法直接进入运行状态。而就绪和运行状态之间的转换一般不受程序控制,而是由系统线程调度所决定。当处于就绪状态的线程得到处理器资源时,该线程进入运行状态;当处于运行状态的线程失去处理器资源时,该线程进入就绪状态。但有一个方法例外,调用yield()方法可让运行状态的线程转入就绪状态。关于yield()方法后面有更详细的介纽。

3、线程死亡

3.1 死亡状态

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

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

线程抛出一个未捕获的ExceptionError

直接调用该线程stop()方法来结束该线程——该方法容易致使死锁,一般不推荐使用。

3.2 程序设计

主线程结束时,其余线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受主线程的影响。为了测试某个线程是否已经死亡,能够调用线程对象的isAlivc()方法,当线程处于就绪运行阻塞了种状态时,该方法将返回true;当线程处于新建死亡状态时,该方法将返回false

不要试图对一个已经死亡的线程调用start()方法使它从新启动,死亡就是死亡,该线程将不可再次做为线程执行。

下面程序尝试对处于死亡状态的线程再次调用start()。

  1. package test;
  2. public class StartDead extends Thread {
  3.    private int i;
  4.  
  5.    // 重写 run方法,run方法的方法体就是线程执行体
  6.    public void run() {
  7.       for (; i < 100; i++) {
  8.          System.out.println(getName() + "" + i);
  9.       }
  10.    }
  11.  
  12.    public static void main(String[] args) {
  13.       // 建立线程对象
  14.       StartDead sd = new StartDead();
  15.       for (int i = 0; i < 300; i++) {
  16.          // 调用 ThreadcurrentThread方法获取当前线程
  17.          System.out.println(Thread.currentThread().getName() + "" + i);
  18.          if (i == 20) {
  19.             // 启动线程
  20.             sd.start();
  21.             // 判断启动后线程的 isAlive()值,输出true
  22.             System.out.println(sd.isAlive());
  23.          }
  24.          // 只有当线程处于新建、死亡两种状态时 isAlive()方法返回false
  25.          // i > 20,则该线程确定已经启动过了,若是sd.isAlive()为假时,
  26.          // 那只能是死亡状态了。
  27.          if (i > 20 && !sd.isAlive())
  28.  
  29.          {
  30.             // 试图再次启动该线程
  31.             sd.start();
  32.          }
  33.       }
  34.    }
  35. }

3.3 运行结果

main 0

main 1

main 2

main 3

main 4

main 5

main 6

main 7

main 8

main 9

main 10

main 11

main 12

main 13

main 14

main 15

main 16

main 17

main 18

main 19

main 20

true

main 21

…………

Thread-0 0

Thread-0 1

Thread-0 2

Thread-0 3

Thread-0 4

Thread-0 5

Thread-0 6

Thread-0 7

Thread-0 8

………………

Thread-0 92

Thread-0 93

Thread-0 94

Thread-0 95

Thread-0 96

Thread-0 97

Thread-0 98

Thread-0 99

main 25

Exception

 

main→主线程

Thread-0→线程1

Exception→异常

上面程序中的粗体字代码试图在线程已死亡的状况下再次调用start()方法来启动该线程。运行上面程序,将引起IllegaIThreadStateException异常,这代表处于死亡状态的线程没法再次运行了。

若是,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
若是,您但愿更容易地发现个人新博客,不妨点击一下左下角的【关注我】。
若是,您对个人博客所讲述的内容有兴趣,请继续关注个人后续博客,我是【Sunddenly】。

本文版权归做者和博客园共有,欢迎转载,但未经做者赞成必须保留此段声明,且在文章页面明显位置给出原文链接,不然保留追究法律责任的权利

相关文章
相关标签/搜索