• 06_线程的生命周期及状态


    【线程状态】

    在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Bolcked)、死亡(Dead)总共5种状态。

    尤其在线程启动之后,它不可能一直占着CPU运行,所以CPU需要在多个线程之间相互切换,于是线程的状态也会多次在运行、阻塞之间切换。

    【新建和就绪状态】

    新建状态:当程序使用new关键字创建了一个线程之后,这个线程就处于新建状态,此时,它和一般的java对象没有区别,仅仅由java虚拟机为其分配内存,并初始化其成员变量值。此时的线程对象没有表现出任何线程的动态特性,程序也不会执行线程的执行体。

    就绪状态:当线程对象调用了start()方法之后,该线程就处于就绪状态,就绪状态相当于"等待执行"。java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,只是表示线程可以运行,但是进入运行状态取决于JVM里线程调度器的调度。如下情况会进入就绪状态;

    1.调用sleep()方法到了指定的时间

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

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

    4.线程正在等待某个通知时,其它线程发出了一个通知。

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

    [ 注意 ]

    启动线程使用 start()方法,不是run()方法!!永远不要调用线程对象的run()方法!!!

    调用start()方法,系统会把run()方法当成线程执行体来处理。

    直接调用run()方法,则run()方法会立即被执行,系统会把线程对象当成一个普通对象来处理,run()方法也变成了一个普通的方法

     另外,直接调用线程对象的run()方法,则run()方法内不能通过getName()来获得当前执行线程的名字,而是需要使用Thread.currentThread先获得当前线程,再调用线程的getName()方法来获得线程的名字。

    另外,调用了线程的run()方法之后,该线程已经不再处于新建状态,不要再次调用线程对象的start()方法,只能对处于新建状态的线程调用start()方法,否则会引发IllegalThreadStateException异常。

    [ 小技巧 ]

    如果希望某一线程对象在调用了start()方法后立即执行,可以使用Thread.sleep(1)让当前执行的其它线程(如主线程)睡眠1毫秒,在这1毫秒内,CPU不会空闲,它会立即去执行处于就绪状态的线程,这样就可以让某一线程立即开始执行。

    【运行和阻塞状态】

    运行状态:处于就绪状态的线程获得了CPU的执行权,开始执行run()方法的线程执行体,则该线程处于运行状态。

    阻塞状态:发生以下几种情况,线程会进入阻塞状态:

    1.线程调用了sleep()方法主动放弃所占用的系统资源

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

    3.线程试图获得一个同步同步监视器,但该同步监视器被其它线程所持有。

    4.线程正在等待某个通知(notify或notifyAll)。

    5.程序调用了线程的suspend()方法将该线程挂起,但该方法会导致死锁,要避免使用。

    [ 关于抢占式策略 ]

    当一个线程开始运行之后,它不会一直处于运行状态,线程在运行过程中需要被中断,目的是使其它的线程获得执行机会,线程调度的细节取决于底层所用策略。对于采用抢占式策略的系统而言,系统会给每个可执行的线程一个小时间段来处理任务,当该时间段用完之后,系统会让该线程进入就绪状态,让所有线程抢占接下来的执行机会,当然,系统会考虑线程的优先级。

    当前正在执行的线程被阻塞之后,其它线程也可以获得执行机会,被阻塞的状态在合适的时候也会进入就绪状态(注意是就绪状态,NOT执行状态),即被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。

    阻塞——>就绪——>运行

    [ 线程状态转换图 ]

     

    从图中可以看出,线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。

    而就绪状态和运行状态之间的转换通常不受程序控制,而是由系统线程调度所决定,处于就绪状态的线程后的处理器的执行权后,线程进入运行状态。

    当处于运行状态的线程失去处理器资源时,该线程进入就绪状态,有一个方法例外,即yield()方法可以让运行状态直接转入就绪状态。

    【线程死亡】

    线程在三种情况下会进入死亡状态:

    1.run()方法或call()方法执行结束后,线程正常结束死亡。

    2.线程抛出一个未捕获的Exception或Error。

    3.直接调用该线程的stop()方法来结束该线程,但是stop()方法容易导致死锁,最好不要用。

    [ 提示 ]

    测试某个线程是否已经死亡,可以调用对象的isAlive()方法,

    当线程处于就绪、运行、阻塞三种状态时,返回true

    当线程处于新建、死亡状态时,返回false

    [ 注意 ]

    不要试图对一个已经死亡的线程调用start()方法使它重启,死亡就是死亡,该线程不可再次作为线程执行,否则会抛出IllegalThreadStateException异常,

    新建状态的线程两次调用start()方法也是错误的额,都会引发IllegalThreadStateException异常。

  • 相关阅读:
    []*T *[]T *[]*T 傻傻分不清楚
    Linux设备树
    gdb使用
    LockBit 2.0 勒索软件全球肆虐,我们该如何防范?
    记一次粗浅的钓鱼样本分析过程
    部分sql注入总结
    Linux系统取证简述
    对抗样本攻击及防御实战
    区块链是否真的安全?黑客盗取价值6亿美金数字货币
    某团购CMS的SQL注入漏洞代码审计
  • 原文地址:https://www.cnblogs.com/HigginCui/p/5903012.html
Copyright © 2020-2023  润新知