java多状态流转如何设计_Java 并发编程 ② - 线程生命周期与状态流转

0293a6900025e4e5dc464ebc52fe2955.png

前言

往期文章:

继上一篇结尾讲的,这一篇文章主要是讲线程的生命周期以及状态流转。主要内容有:

Java 中对线程状态的定义,与操作系统线程状态的对比

线程状态的流转图

如何自己验证状态的流转

一、Java 线程的状态

先来谈一谈Java 中线程的状态。在 java.lang.Thread.State 类是 Thread的内部枚举类,在里面定义了Java 线程的六个状态,-注释信息也非常的详细。

public enum State {

/**

* Thread state for a thread which has not yet started.

* 初始态,代表线程刚创建出来,但是还没有 start 的状态

*/

NEW,

/**

* Thread state for a runnable thread. A thread in the runnable

* state is executing in the Java virtual machine but it may

* be waiting for other resources from the operating system

* such as processor.

*

* 运行态,代表线程正在运行或者等待操作系统资源,如CPU资源

*/

RUNNABLE,

/**

* 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}.

*

* 阻塞态,代表线程正在等待一个监视器锁(即我们常说的synchronized)

* 或者是在调用了Object.wait之后被notify()重新进入synchronized代码块

*/

BLOCKED,

/**

* Thread state for a waiting thread.

* A thread is in the waiting state due to calling one of the

* following methods:

*

*

{@link Object#wait() Object.wait} with no timeout

*

{@link #join() Thread.join} with no timeout

*

{@link LockSupport#park() LockSupport.park}

*

*

*

A thread in the waiting state is waiting for another thread to

* perform a particular action.

*

* For example, a thread that has called Object.wait()

* on an object is waiting for another thread to call

* Object.notify() or Object.notifyAll() on

* that object. A thread that has called Thread.join()

* is waiting for a specified thread to terminate.

*

* 等待态,调用以下方法会进入等待状态:

* 1. 调用不会超时的Object.wait()方法

* 2. 调用不会超时的Thread.join()方法

* 3. 调用不会超时的LockSupport.park()方法

*/

WAITING,

/**

* Thread state for a waiting thread with a specified waiting time.

* A thread is in the timed waiting state due to calling one of

* the following methods with a specified positive waiting time:

*

*

{@link #sleep Thread.sleep}

*

{@link Object#wait(long) Object.wait} with timeout

*

{@link #join(long) Thread.join} with timeout

*

{@link LockSupport#parkNanos LockSupport.parkNanos}

*

{@link LockSupport#parkUntil LockSupport.parkUntil}

*

*

* 超时等待态,在调用了以下方法后会进入超时等待状态

* 1. Thread.sleep()方法后

* 2. Object.wait(timeout)方法

* 3. Thread.join(timeout)方法

* 4. LockSupport.parkNanos(nanos)方法

* 5. LockSupport.parkUntil(deadline)方法

*/

TIMED_WAITING,

/**

* Thread state for a terminated thread.

* The thread has completed execution.

*

* 终止态,代表线程已经执行完毕

*/

TERMINATED;

}

关于上面JDK源码中对于BLOCKED状态的注释,这里有一点需要补充的,就是如果是线程调用了Object.wait(timeout)方法进入TIMED_WAITING状态之后,如果是因为超过指定时间,脱离TIMED_WAITING状态,如果接下去线程是要重新进入synchronize 代码块的话,也是会先进入等待队列,变成BLOCKED状态,然后请求监视器锁资源。

1.1 操作系统中的线程状态

再来看,操作系统层面,线程存在五类状态,状态的流转关系可以参考下面的这张图。

904217155e897e1396163101e73341e5.png

可以看到,Java 中所说的线程状态和操作系统层面的线程状态是不太一样的。

Java 中的 RUNNABLE 其实包含了OS中的RUNNING和READY

Java 中的WAITING、TIMED_WAITING、BLOCKED其实是对OS中WAITING状态的一个更细致的划分

在Thread.State源码中也写了这么一句话:

These states are virtual machine states which do not reflect any operating system thread states.

这些状态只是线程在虚拟机中的状态,并不反映操作系统的线程状态。

对于这两个层面对比,你需要知道的是,Java的线程状态是服务于虚拟机的。从这个角度来考虑的话,把底层OS中的RUNNING和READY状态映射上来也没多大意义,因此,统一成为RUNNABLE 状态是不错的选择,而对WAITING状态更细致的划分,也是出于这么一个考虑。

二、状态流转图

9842bf9108049f824d4acb0900703a19.png

图很详细,结合前面的内容一起食用。

关于阻塞状态,这里还要多说几句话,我们上面说的,都是在JVM 代码层面的实际线程状态。但是在一些书比如《码出高效》中,会把Java 线程的阻塞状态分为:

同步阻塞:即锁被其他线程占用

主动阻塞:指调用了Thread 的某些方法,主动让出CPU执行权,比如sleep()、join()等

等待阻塞:执行了wait()系列方法

三、测试

这里演示一下,如何在IDEA 上面来验证上述的状态流转。有疑问或者有兴趣的读者可以按照同样的方法来验证。

我这里想要用代码验证下面的情况,

就是如果是线程1调用了Object.wait(timeout)方法进入TIMED_WAITING状态之后,如果是因为超过指定时间,脱离TIMED_WAITING状态,如果接下去线程是要重新进入synchronize 代码块的话,也是会先进入等待队列,变成BLOCKED状态,然后请求监视器锁资源。

public class ThreadLifeTempTest {

public static void main(String[] args) {

Object object = new Object();

new Thread(()->{

synchronized (object) {

try {

System.out.println("thread1 waiting");

// 等待10s,进入Timed_Waiting

// 10s 后会进入Blocked,获取object的监视器锁

object.wait(10000);

System.out.println("thread1 after waiting");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}, "Thread1").start();

new Thread(()->{

synchronized (object) {

try {

// sleep也不会释放锁,所以thread1 不会获取到锁

Thread.sleep(10000000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}, "Thread2").start();

}

}

使用IDEA的RUN模式运行代码,然后点击左边的一个摄像头按钮(dump thread),查看各线程的状态。

9bfdbb7052996bdc894173a024e071a2.png

在Thread 1 等待 10s中时,dump的结果:Thread 1和Thread 2都处于TIMED_WAITING状态,

"Thread2" #13 prio=5 os_prio=0 tid=0x0000000020196800 nid=0x65b8 waiting on condition [0x0000000020afe000]

java.lang.Thread.State: TIMED_WAITING (sleeping)

at java.lang.Thread.sleep(Native Method)

at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$1(ThreadLifeTempTest.java:33)

- locked <0x000000076b71c748> (a java.lang.Object)

at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$2/1096979270.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"Thread1" #12 prio=5 os_prio=0 tid=0x0000000020190800 nid=0x25fc in Object.wait() [0x00000000209ff000]

java.lang.Thread.State: TIMED_WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x000000076b71c748> (a java.lang.Object)

at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$0(ThreadLifeTempTest.java:21)

- locked <0x000000076b71c748> (a java.lang.Object)

at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$1/1324119927.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

在Thread 1 等待 10s之后,Thread 1重新进入synchronize 代码块,进入等待队列,变成BLOCKED状态

"Thread2" #13 prio=5 os_prio=0 tid=0x0000000020196800 nid=0x65b8 waiting on condition [0x0000000020afe000]

java.lang.Thread.State: TIMED_WAITING (sleeping)

at java.lang.Thread.sleep(Native Method)

at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$1(ThreadLifeTempTest.java:33)

- locked <0x000000076b71c748> (a java.lang.Object)

at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$2/1096979270.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

"Thread1" #12 prio=5 os_prio=0 tid=0x0000000020190800 nid=0x25fc waiting for monitor entry [0x00000000209ff000]

java.lang.Thread.State: BLOCKED (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x000000076b71c748> (a java.lang.Object)

at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$0(ThreadLifeTempTest.java:21)

- locked <0x000000076b71c748> (a java.lang.Object)

at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$1/1324119927.run(Unknown Source)

at java.lang.Thread.run(Thread.java:748)

小结

在本篇文章中,主要讲解了线程的生命周期,各个状态以及状态流转。如果对线程状态的变化还有不了解的,可以借助最后一部分的测试方法来实际验证,帮助理解。

下一章,内容是介绍ThreadLocal 和 InheritableThreadLocal 的用法和原理,感兴趣请持续关注。

如果本文有帮助到你,希望能点个赞,这是对我的最大动力????。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内容简介: 设计模式是一套被反复使用、多数人知晓的、经过分编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 本课程内容定位学习设计原则,学习设计模式的基础。在实际开发过程中,并不是一定要求所有代码都遵循设计原则,我们要考虑人力、时间、成本、质量,不是刻意追求完美,要在适当的场景遵循设计原则,体现的是一种平衡取舍,帮助我们设计出更加优雅的代码结构。本章将详细介绍开闭原则(OCP)、依赖倒置原则(DIP)、单一职责原则(SRP)、接口隔离原则(ISP)、迪米特法则(LoD)、里氏替换原则(LSP)、合成复用原则(CARP)的具体内容。 为什么需要学习这门课程? 你在日常的开发中,会不会也遇到过同样的问题。系统出现问题,不知道问题究竟出在什么位置;当遇到产品需求,总是对代码缝缝补补,不能很快的去解决。而且平时工作中,总喜欢把代码堆在一起,出现问题时,不知道如何下手,工作效率很低,而且自己的能力也得不到提升。而这些都源于一个问题,那就是软件设计没做好。这门课能帮助你很好的认识设计模式,让你的能力得到提升。课程大纲: 为了让大家快速系统了解设计模式知识全貌,我为您总结了思维导图,帮您梳理学习重点,建议收藏!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值