IT老哥,一个在大厂做高级Java开发的程序员,每天分享技术干货文章
本章学习完成,你将会对线程
的生命周期
有清楚的认识,并且明白不同状态
之间是如何转换的,以及对java线程状态枚举类解读。
熟悉这块的同学可以选择直接点赞?完成本章学习哦
一个完整的线程生命周期,要经过 新建
(new)、就绪
(Runnable)、运行
(Running)、阻塞
(Blocked)和死亡
(Dead)5种状态。
new阶段就是你new Thread()创建线程对象时候的阶段。
new阶段下其实线程根本还是不存在
的,我们只是创建了一个Therad对象
,就和我们最常用的new关键字是一个道理。
只有当我们真正把线程启动起来的时候,此时才会在JVM进程
中把我们的线程创建出来。
我们new了一个Thread对象之后就需要调用Thread.start()来启动线程,此时线程会从new
阶段转换到Runnable
阶段。
只有调用Thread.start()方法才能使线程从new阶段转换到Runnable阶段。
当然我们从字面意思也可以知道此时线程是处于可执行转状态
而不是真正的执行中
状态了,此时的线程只能等CPU翻牌子,翻到了他才能真正的跑起来。
有些同学可能会说要是CPU一直不翻牌子咋办?严格意义上来讲,处在Runnable的线程只有两条出路,一条是线程意外退出
,还有一条是被CPU翻牌子进入Running
阶段。
new
阶段的时候我们的线程还是宫外
的一位佳人
对象宫里
的一位小主
了,也就是中间阶段Runnable
,CPU调度执行权
的时候就晋升为得宠的娘娘
了,也就是进入了Running
阶段。可想而知,此时我们的线程娘娘一定是比宫外的佳人要复杂的多了。⚠️注意
有了解过这块内容的同学看到这里可能会有疑问,java线程状态中并没有这个状态
,为什么我们在讲生命周期的时候会把这一状态单独拆分出来做讲解?为了章节内容的流畅性,这块内容的解释放到下一节去讲解,这边我们还是继续讲我们的线程生命周期。
好的我们继续
这个阶段的线程已经获取到了CPU调度执行权,也就是说处于运行中状态了。
在该阶段中,线程可以向前或者向后发生转换:
Runnable
阶段。Runnable
阶段(这种方式并不是百分百生效的,在CPU资源不紧张的时候不会生效)。Blocked
阶段(这里讲的Blocked阶段和线程的Blocked状态需要区分开,这边讲的是一个比较广义的Blocked的阶段)Blocked
阶段Blocked
阶段Terminated
阶段进入该阶段的原因已经在Running阶段阐述过了,这里就不再说明,这里主要介绍一下处于该阶段的线程可以如何切换。
Terminated
,比如调用stop
方法或者意外死亡(JVM Crash
)阻塞
的操作结束,读取或者写入了想操作的数据进入Runnable
状态休眠
,进入Runnable
状态notify
或者notifyall
唤醒,进入Runnable
状态锁资源
,进入Runnable
状态被打断
,比如调用interrupt
方法,进入Runnable
状态Terminated
状态是一个线程的最终状态,在该状态中线程不会切换到其他任何状态。
线程进入Terminated
状态意味着线程整个生命周期结束了,进入Terminated状态的方式有以下三种
:
请同学们先找到java.lang.Thread.State这个枚举类。
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种线程状态
,根据说明我们一一来解读这六个状态。
* <li>{@link #new}<br>
* A thread that has not yet started is in this state.
* </li>
源码清楚地说明了new状态
就是一个线程刚刚被创建
,但是还没有启动地时候所处的状态,这个和我们上一小节中地new阶段能够对应起来这里就不多说了。
* <li>{@link #Runnable}<br>
* A thread executing in the Java virtual machine is in this state.
* </li>
这段的说明意思是在java虚拟机中执行的线程
所处的状态称之为Runnable
。
也就是说我们上一节中分开讲解的Runnable
阶段和Running
阶段在线程状态中来看统一都称之为Runnable
状态。
之所以我们在生命周期划分的时候把这两个状态拆分开来看是因为这两个状态差别还是很大的,Running
状态的线程比Runnable
状态的线程更加复杂
一些。
/*
* <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状态的描述如下:
/**
* 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状态」
/**
* 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.wait
、Thread.join
和LockSupport.park
三个API接口才会进入waiting状态。
唤醒方式
是等待其他线程完成自己逻辑之后,调用notify
或者notiffyall
唤醒处于waiting
状态的线程。
到这里同学们应该已经清楚了线程生命周期
以及各个阶段
之间的转换,同时明白了不同阶段的生命周期
所对应的java线程状态
。
云服务器,云硬盘,数据库(包括MySQL、Redis、MongoDB、SQL Server),CDN流量包,短信流量包,cos资源包,消息队列ckafka,点播资源包,实时音视频套餐,网站管家(WAF),大禹BGP高防(包含高防包及高防IP),云解析,SSL证书,手游安全MTP,移动应用安全、 云直播等等。
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;
}