1.3 多线程 - 线程的生命周期

线程状态转换图

当前线程启动以后,不可能一直“霸占” CPU 独立运行,所以 CPU 需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。

新建与就绪状态

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

当线程对象调用 start() 方法之后,该线程处于就绪状态,Java 虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,只是表示该线程可以运行了。至于该线程何时开始运行,取决于 JVM 里线程调度器的调度。

启动线程使用 start() 方法,而不是 run() 方法!调用 start() 方法来启动线程,系统会把该 run() 方法当成线程执行体来处理;但如果直接调用线程对象的 run() 方法,则 run() 方法立即就会被执行,而且在 run() 方法返回其他线程无法并发执行 —— 也就是说,如果直接调用线程对象的 run() 方法,系统把线程对象当成一个普通对象,而 run() 方法也是一个普通方法,而不是线程执行体。且不能直接通过 getName() 获取当前执行线程的名字,而是需要使用 Thread.currentThread() 方法先获取当前线程,再调用线程对象的 getName() 方法获取线程的名字。

start() 方法的执行过程

Thread源码本身的 run() 方法:

private Runnable target;

@Override
public void run() {
      if (target != null) {
            target.run();
      }
}

target参数在Thread构造函数中被赋值,如果执行的线程为继承的 Thread,则线程启动执行子类覆盖的 run() 方法体,如果是实现的 Runnable 接口,执行的则是实现类的 run() 方法;看代码:

public class FirstThread extends Thread {
    
    private int i;
    
    public FirstThread() {} 
    
    public FirstThread(Runnable r) {
        super(r) ;
    }
    
    // 重写run方法,run方法的方法体就是线程执行体
    public void run() {
        super.run();    // 同时执行 Thread 的run() 方法才会执行 Runnable 接口实现类的run() 方法
        for (; i < 5; i++) {
            System.err.println("FirstThread:"+getName() + " " + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 2) {
                new FirstThread().start();
                new FirstThread(new SecondThread()).start(); // 如果子类run() 方法未有super.run(),则Runnable接口不会执行
                new Thread(new SecondThread()).start();
            }
        }
    }
}

具体内容可参考:Java 中的进程与线程

调用线程对象的 start() 方法之后,该线程立即进入就绪状态 —— 即“等待状态”,但该线程并未真正进入运行状态。只能对处于新建状态的线程调用 start() 方法,否则将引发 IllegalThreadStateException 异常。

运行和阻塞状态

  • 就绪状态的线程获取CPU,执行run()方法的线程执行体,线程处于运行状态;
  • 一个CPU在任何时刻只有一个线程处于运行状态
  • 一个线程不可能一直处于运行状态(除非执行体很短),线程在运行期间可能会中断;
  • 当一个线程中断,另一个线程获得执行机会,这种线程调度细节取决于底层平台所采用的策略;
    • 抢占式策略:给每个线程分配小时间段处理任务,该时间段用完,系统就会剥夺线程所占用的资源,让其他线程获得执行机会;在选择下一个线程时,系统会考虑线程的优先级。
    • 协作式调度策略:只有当一个线程调用了它的 sleep() 或 yield() 方法后才会放弃所占用的资源,即由线程主动放弃所占用的资源。

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

  • 1、线程调用 sleep() 方法主动放弃所占用的处理资源;
  • 2、线程调用了一个阻塞式 IO 方法,在该方法返回之前,该线程被阻塞;
  • 3、线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有;
  • 4、线程在等待某个通知(notify)
  • 5、程序调用了线程的 suspend() 方法将该线程挂起

发生如下情况时,线程将会进入就绪状态

  • 1、调度 sleep() 方法的线程经过了指定时间;
  • 2、线程调用的阻塞式 IO 方法已经返回;
  • 3、线程成功地获得了试图取得同步监视器;
  • 4、线程正在等待某个通知时,其他线程发出了一个通知;
  • 5、处于挂起状态的线程被调用了 resume() 恢复方法。
线程状态转换图

从图可知,线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。而就绪和运行状态之间的转换通常不受程序控制,而是由系统线程调度所决定,当处于就绪状态的线程获得处理器资源时,该线程进入运行状态;当处于运行状态的线程失去处理器资源时,该线程进入就绪状态。但有一个方法例外,调用 yield() 方法可以让运行状态的线程转入就绪状态。

线程死亡

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

  • run() 或 call() 方法执行完成,线程正常结束
  • 线程抛出一个未捕捉的 Exception 或 Error
  • 直接调用该线程的 stop() 方法来结束该线程 —— 该方法容易导致死锁,通常不推荐使用

当主线程结束时,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受主线程的影响。

测试某个线程是否已经死亡,可以调用线程对象的 isAlive() 方法,当线程处于就绪、运行、阻塞三种状态时,该方法将返回 true;当线程处于新建、死亡两种状态时,该方法将返回 false。

Java中可以通过 interrupt() 中断线程,并通过 interrupted() 方法来判断线程是否已经中断,来结束线程 run() 执行体,从而终止当前线程。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 来源: https://www.cnblogs.com/albertrui/p/8383799.html 一、前言...
    青青子衿zq阅读 520评论 0 0
  • 林炳文Evankaka原创作品。转载自http://blog.csdn.net/evankaka 本文主要讲了ja...
    ccq_inori阅读 645评论 0 4
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,949评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,438评论 1 15
  • 摘要 在线程被创建并启动以后,并不是立马就进入了执行状态,也不是一直处于执行状态之中。因为CPU需要在不同的线程之...
    胖瘦馒头阅读 262评论 0 0