前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程生命周期,五大状态转换分析

线程生命周期,五大状态转换分析

作者头像
公众号 IT老哥
修改2020-09-21 16:29:26
5080
修改2020-09-21 16:29:26
举报
文章被收录于专栏:用户7621540的专栏

本文源自 公-众-号 IT老哥 的分享

IT老哥,一个在大厂做高级Java开发的程序员,每天分享技术干货文章

前言

本章学习完成,你将会对线程生命周期有清楚的认识,并且明白不同状态之间是如何转换的,以及对java线程状态枚举类解读。

熟悉这块的同学可以选择直接点赞?完成本章学习哦

一、线程生命周期

一个完整的线程生命周期,要经过 新建(new)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。

1.new阶段

new阶段就是你new Thread()创建线程对象时候的阶段。

划重点了

new阶段下其实线程根本还是不存在的,我们只是创建了一个Therad对象,就和我们最常用的new关键字是一个道理。

只有当我们真正把线程启动起来的时候,此时才会在JVM进程中把我们的线程创建出来。

我们new了一个Thread对象之后就需要调用Thread.start()来启动线程,此时线程会从new阶段转换到Runnable阶段。

2. Runnable阶段

只有调用Thread.start()方法才能使线程从new阶段转换到Runnable阶段。

当然我们从字面意思也可以知道此时线程是处于可执行转状态而不是真正的执行中状态了,此时的线程只能等CPU翻牌子,翻到了他才能真正的跑起来。

有些同学可能会说要是CPU一直不翻牌子咋办?严格意义上来讲,处在Runnable的线程只有两条出路,一条是线程意外退出,还有一条是被CPU翻牌子进入Running阶段。


可以打个比方:
  • new阶段的时候我们的线程还是宫外的一位佳人对象
  • 调用start方法之后就摇身一变成为宫里的一位小主了,也就是中间阶段Runnable
  • 等到获取到CPU调度执行权的时候就晋升为得宠的娘娘了,也就是进入了Running阶段。可想而知,此时我们的线程娘娘一定是比宫外的佳人要复杂的多了。

3.Running阶段

⚠️注意

有了解过这块内容的同学看到这里可能会有疑问,java线程状态中并没有这个状态,为什么我们在讲生命周期的时候会把这一状态单独拆分出来做讲解?为了章节内容的流畅性,这块内容的解释放到下一节去讲解,这边我们还是继续讲我们的线程生命周期。


好的我们继续

这个阶段的线程已经获取到了CPU调度执行权,也就是说处于运行中状态了。

在该阶段中,线程可以向前或者向后发生转换:

  • 由于CPU的调度器轮询导致该线程放弃执行,就会进入Runnable阶段。
  • 线程主动调用yield,放弃CPU执行权,就会进入Runnable阶段(这种方式并不是百分百生效的,在CPU资源不紧张的时候不会生效)。
  • 调用sleep、wait方法,进入Blocked阶段(这里讲的Blocked阶段和线程的Blocked状态需要区分开,这边讲的是一个比较广义的Blocked的阶段)
  • 进行某个阻塞的IO操作而进入Blocked阶段
  • 为了获取某个锁资源而加入到该锁到阻塞队列中而进入Blocked阶段
  • 线程执行完成或者调用stop方法或者判断某个逻辑标识,直接进入Terminated阶段

4.Blocked阶段

进入该阶段的原因已经在Running阶段阐述过了,这里就不再说明,这里主要介绍一下处于该阶段的线程可以如何切换。

  • 直接进入Terminated,比如调用stop方法或者意外死亡(JVM Crash
  • 线程阻塞的操作结束,读取或者写入了想操作的数据进入Runnable状态
  • 线程完成了指定时间的休眠,进入Runnable状态
  • Wait状态的线程被notify或者notifyall唤醒,进入Runnable状态
  • 获取到了锁资源,进入Runnable状态
  • 线程阻塞过程被打断,比如调用interrupt方法,进入Runnable状态

5.Terminated状态

Terminated状态是一个线程的最终状态,在该状态中线程不会切换到其他任何状态。

线程进入Terminated状态意味着线程整个生命周期结束了,进入Terminated状态的方式有以下三种

  • 线程正常结束
  • 线程意外结束
  • JVM Crash

线程生命周期和java线程状态对应:源码分析

请同学们先找到java.lang.Thread.State这个枚举类。

代码语言:javascript
复制
public enum State {

  // 新建,未调用start方法
  new,
  
  // 包含两种状态:就绪状态、运行状态
  Runnable,
  
  // 阻塞
  Blocked,
  
  /**
   * 等待态
   * 3种情况调用后会导致线程处于这个状态:
   * 1.Object.wait
   * 2.Thread.join
   * 3.LockSupport.park
   * 等待态的线程会等待其他线程执行特定的操作
   * 例如一个线程调用了Object.wait之后进入等待态,另一个线程调用Object.notify或Object.notifyAll可以将其唤醒,被唤醒的线程需要获取对象的锁才能恢复执行
   * 调用Thread.join会等待指定的线程终止
   */
  waiting,
  
  /**
   * 超时等待态
   * 线程等待指定的时间再执行
   * 5种情况调用后会导致线程处于这个状态:
   * 1.Thread.sleep
   * 2.Object.wait 传入等待时长
   * 3.Thread.join 传入等待时长
   * 4.LockSupport.parkNanos
   * 5.LockSupport.parkUntil
   */
  timed_waiting,
  
  // 终止,线程执行完毕
  Terminated;
}

可以看到这边state枚举类包含有6种线程状态,根据说明我们一一来解读这六个状态。

new状态 = new阶段
代码语言:javascript
复制
* <li>{@link #new}<br>
* A thread that has not yet started is in this state.
* </li>

源码清楚地说明了new状态就是一个线程刚刚被创建,但是还没有启动地时候所处的状态,这个和我们上一小节中地new阶段能够对应起来这里就不多说了。

(2)Runnable状态 = Runnable阶段+Running阶段
代码语言:javascript
复制
* <li>{@link #Runnable}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>

这段的说明意思是在java虚拟机中执行的线程所处的状态称之为Runnable

也就是说我们上一节中分开讲解的Runnable阶段和Running阶段在线程状态中来看统一都称之为Runnable状态。

之所以我们在生命周期划分的时候把这两个状态拆分开来看是因为这两个状态差别还是很大的,Running状态的线程比Runnable状态的线程更加复杂一些。

Blocked状态+waiting状态+timed_waiting状态 = Blocked阶段
代码语言:javascript
复制
/*
 * <li>{@link #Blocked}<br>
 *     A thread that is blocked waiting for a monitor lock
 *     is in this state.
 *     </li>
 * <li>{@link #waiting}<br>
 *     A thread that is waiting indefinitely for another thread to
 *     perform a particular action is in this state.
 *     </li>
 * <li>{@link #TIMED_waiting}<br>
 *     A thread that is waiting for another thread to perform an action
 *     for up to a specified waiting time is in this state.
 *     </li>

源码中对于这三个状态对叙述是这样的

Blocked:等待获取监视器锁而进入阻塞队列的线程所处的状态

waiting:无限期等待另一线程执行特定操作的线程处于此状态。

TIMED_waiting:等待另一线程执行操作的线程在指定的等待时间内处于此状态。

我们还是通过源码中对于state枚举值的描述来进入主题。state中对于Blocked状态的描述如下:

代码语言:javascript
复制
 /**
   * Thread state for a thread blocked waiting for a monitor lock.
   * A thread in the blocked state is waiting for a monitor lock
   * to enter a synchronized block/method or
   * reenter a synchronized block/method after calling
   * {@link Object#wait() Object.wait}.
   */
  Blocked

源码中对于Blocked这一状态的叙述是这样的,线程在等待获取一个monitor lock的时候该线程就是处于Blocked

说通俗一点就是被synchronized关键字描述的同步块或者方法,此时其他线程想要获取这个被描述的同步块或者方法的时候。

这个线程就会进去等待获取monitor lock,也就是进入到Blocked状态。这里的关键是monitor lock监视锁。

唤醒方式是目标监视锁monitor lock主动释放,这时候我们去竞争这个监视锁并成功之后。

「waiting状态」

代码语言:javascript
复制
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
*   <li>{@link Object#wait() Object.wait} with no timeout</li>
*   <li>{@link #join() Thread.join} with no timeout</li>
*   <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
waiting

这里给我们描述了哪些情况下我们的线程会进入waiting状态,分别是调用Object.waitThread.joinLockSupport.park三个API接口才会进入waiting状态。

唤醒方式是等待其他线程完成自己逻辑之后,调用notify或者notiffyall唤醒处于waiting状态的线程。

Terminated状态 = Terminated阶段

到这里同学们应该已经清楚了线程生命周期以及各个阶段之间的转换,同时明白了不同阶段的生命周期所对应的java线程状态

云服务器云硬盘数据库(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos资源包,消息队列ckafka,点播资源包,实时音视频套餐,网站管家(WAF),大禹BGP高防(包含高防包及高防IP),云解析SSL证书,手游安全MTP移动应用安全云直播等等。

线程相关方法

代码语言:javascript
复制
public class Thread{
    // 线程的启动
    public void start(); 
    // 线程体
    public void run(); 
    // 已废弃
    public void stop(); 
    // 已废弃
    public void resume(); 
    // 已废弃
    public void suspend(); 
    // 在指定的毫秒数内让当前正在执行的线程休眠
    public static void sleep(long millis); 
    // 同上,增加了纳秒参数
    public static void sleep(long millis, int nanos); 
    // 测试线程是否处于活动状态
    public boolean isAlive(); 
    // 中断线程
    public void interrupt(); 
    // 测试线程是否已经中断
    public boolean isInterrupted(); 
    // 测试当前线程是否已经中断
    public static boolean interrupted(); 
    // 等待该线程终止
    public void join() throws InterruptedException; 
    // 等待该线程终止的时间最长为 millis 毫秒
    public void join(long millis) throws InterruptedException; 
    // 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒
    public void join(long millis, int nanos) throws InterruptedException; 
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT老哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 本文源自 公-众-号 IT老哥 的分享
  • 前言
  • 一、线程生命周期
    • 1.new阶段
      • 划重点了
    • 2. Runnable阶段
      • 可以打个比方:
    • 3.Running阶段
      • 4.Blocked阶段
        • 5.Terminated状态
          • new状态 = new阶段
          • (2)Runnable状态 = Runnable阶段+Running阶段
          • Blocked状态+waiting状态+timed_waiting状态 = Blocked阶段
          • Terminated状态 = Terminated阶段
      • 线程生命周期和java线程状态对应:源码分析
      • 线程相关方法
      相关产品与服务
      SSL 证书
      腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档