Java并发 -- 线程生命周期

1. 初始状态

  • 线程已经被创建,但还不允许分配CPU执行
  • 该状态属于 编程语言 所特有,仅仅在编程语言层面被创建,在操作系统层面,真正的线程还没有创建
    2. 可运行状态
    线程可以分配CPU执行,该状态下真正的操作系统线程已经被创建
    3. 运行状态
  • 当有空闲的CPU时,操作系统会将其分配给处于 可运行状态 的线程,被分配到CPU的线程的状态就转换为 运行状态
  1. 休眠状态
  • 处于 运行状态 的线程如果调用一个 阻塞的API 或者 等待某个事件 ,那么线程状态就会切换为 休眠状态
  • 切换为休眠状态的同时会 释放CPU使用权 , 处于休眠状态的线程永远没有机会获得CPU使用权
  • 当等待的事件出现后,线程就会从休眠状态切换到 可运行状态
  1. 终止状态
  • 线程 执行完 或者 出现异常 就会进入 终止状态 ,处于终止状态的线程不会切换到其它状态
  • 进入终止状态意味着线程生命周期的 结束

简化合并

  1. 通用的线程生命周期里的5种状态在不同的编程语言会有 简化合并
  2. Java把 可运行状态 和 运行状态 合并了
    这两个状态对操作系统调度层是有价值的,但 JVM把线程调度交给了操作系统处理 ,JVM并不关心这两个状态
  3. JVM同时也细化了 休眠状态

Java线程的生命周期

  1. Java线程的状态可以参照代码 java.lang.Thread.State
  2. 在操作系统层,Java线程状态的 BLOCKED、WAITING、TIMED_WAITING 都是 休眠状态 ,永远无法获得CPU的使用权
  3. BLOCKED、WAITING、TIMED_WAITING可以理解为导致线程 进入休眠状态 的三个 原因

RUNNABLE + BLOCKED

  1. 唯一场景: 线程等待synchronized的隐式锁
  2. synchronized修饰的方法(代码块)在同一时刻只允许一个线程执行,其它线程只能等待( RUNNABLE -> BLOCKED )
  3. 当等待的线程获得synchronized隐式锁时, BLOCKED -> RUNNABLE

阻塞式API

  1. 在 操作系统 层面,操作系统线程调用 阻塞式API ,会转换到 休眠状态
  2. 在 JVM 层面,Java线程调用 阻塞式API ,Java线程的状态会 保持RUNNABLE
  • JVM层面并不关心操作系统调度相关的状态
  • 在JVM看来,等待 CPU使用权 (操作系统层面为 可执行状态 )和等待 IO (操作系统层面为 休眠状态 )没有区别
    • 都是在等待某个资源,所以都归入RUNNABLE状态

RUNNABLE + WAITING

  1. 获得synchronized隐式锁的线程,调用无参数的 Object.wait() 方法
  2. 调用无参数的 Thread.join() 方法,join是一种 线程同步 的方式
  • 有线程A,当线程B调用A.join()时,线程B会等待线程A执行完,线程B的状态切换: RUNNABLE -> WAITING
  • 当线程A执行完后,线程B的状态切换: WAITING -> RUNNABLE
  1. 调用 LockSupport.park() 方法, 当前线程会阻塞 ,线程状态切换: RUNNABLE -> WAITING
  • 调用 LockSupport.unpark(Thread thread) 方法可以唤醒 目标线程
  • 目标线程的状态切换: WAITING -> RUNNABLE

RUNNABLE + TIMED_WAITING

  1. 调用 带超时参数 的 Thread.sleep(long millis) 方法
  2. 获得synchronized隐式锁的线程,调用 带超时参数 的 Object.wait(long timeout) 方法
  3. 调用 带超时参数 的 Thread.join(long millis) 方法
  4. 调用 带超时参数 的 LockSupport.parkNanos(Object blocker, long nanos) 方法
  5. 调用 带超时参数 的 LockSupport.parkUntil(long deadline) 方法

NEW + RUNNABLE

// 方式1
class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
    }
}
Thread myThread = new MyThread();

// 方式2
class Runner implements Runnable {
    @Override
    public void run() {
        // task code
    }
}
Thread thread = new Thread(new Runner());
  1. Java刚创建出来的Thread对象就是处于NEW状态,创建Thread对象的两种方式:继承Thread + 实现Runnable
  2. NEW状态的线程, 不会被操作系统调度 ,所以不会执行,调用线程对象的start()方法: NEW -> RUNNABLE

RUNNABLE + TERMINATED

当线程 执行完 run()方法后,会自动切换到TERMINATED状态;在执行run()方法的过程中 抛出异常 ,也会导致线程终止

stop() + interrupt()

  1. stop()方法会 直接杀死线程 ,不给线程喘息的机会
  • 如果线程持有ReentrantLock锁,被stop()的线程 不会自动调用 ReentrantLock.unlock()去释放锁
  • 类似的方法还有suspend()和resume()
  1. interrupt()方法仅仅 通知线程 ,收到通知的线程可以选择 无视 这个通知,继续选择执行后续操作
  2. 被interrupt的线程,收到通知的两种方式: InterruptedException + 主动检测
  3. InterruptedException
  • 当线程A处于 WAITING 或 TIMED_WAITING 状态时,其它线程调用A.interrupt()方法时
    • 会使线程A返回 RUNNABLE 状态,同时线程A的代码会触发InterruptedException异常
    • Thread.sleep(long millis)、Thread.join()、Object.wait()的方法签名都有throw InterruptedException
  • 当线程A处于 RUNNABLE 状态时,并且阻塞在java.nio.channels.InterruptibleChannel上时
    • 如果其它线程调用A.interrupt()方法,线程A会触发java.nio.channels.ClosedByInterruptException
  • 当线程A处于 RUNNABLE 状态时,并且阻塞在java.nio.channels.Selector上时
    • 如果其它线程调用A.interrupt()方法,线程A会立即返回
  • 线程中断状态
    • 线程A抛出InterruptedException后,会 重置线程中断状态
  1. 主动检测
  • 当线程A处于 RUNNABLE 状态,并且 没有阻塞 在某个IO操作上,此时需要依赖线程A 主动检测 自己的中断状态
    • 如果其它线程调用A.interrupt()方法,那么线程A可以通过isInterrupted()方法来检测自己是否被中断了
Thread th = Thread.currentThread();
while (true) {
    if (th.isInterrupted()) {
        // 死循环,永远无法break
        break;
    }
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // 抛出InterruptedException会重置线程中断状态,导致死循环
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容

  • 0 前言 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要...
    七寸知架构阅读 5,190评论 2 63
  • Java多线程学习 [-] 一扩展javalangThread类 二实现javalangRunnable接口 三T...
    影驰阅读 2,952评论 1 18
  • 本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 首先讲...
    李欣阳阅读 2,444评论 1 15
  • 在为自己的故事等待结局 所有的故事也都已启程 却忽然忘了是怎么样的一个开始 在那个斜阳的不再回来的春日 岁月安排了...
    空城锦阅读 179评论 0 1
  • 1、后台接口一直获取不到数据 2、Json Value Should Equal 的使用,如果去校验一个json数...
    张talks阅读 818评论 0 0