图解并发系列-线程的生命周期

简介: 本文将带你了解线程的基础知识点,

线程的生命周期

一个线程从创建,到最后的消亡,是需要经历 多种不同的状态,而这些不同的线程状态,从开始到结束也构成了线程生命周期的不同阶段,如图:

图解高并发系列-线程的生命周期

1) new新建状态

使用new关键字去创建一个线程对象,这仅仅是在堆中给对象分配内存空间,在调用start方法之前的线程所处的状态就是这种状态;线程是没有启动,只是创建一个线程对象放在堆中而已;如:


Thread t=new Thread(); //此时t属于新建状态

当新建状态下的线程对象调用了start方法,该线程对象就从新建状态进入可运行状态(runnable); 线程对象的start方法有且只能调用一次,多次调用会发生IllegalThreadStateException;

2)runnable可运行状态

包括两个:运行状态和就绪状态;

  • 就绪状态:线程对象调用start方法后,就等JVM的调度,但是此时线程还未开始运行;
  • 运行状态:线程对象已经获得JVM调度,就处在运行了;如果存在多个CPU,那就允许多个线程并行运行;

q

2)blocked阻塞状态

处于运行中的线程因为某些原因要放弃CPU时间片,暂时停止运行,会进入阻塞状态;此时JVM不会给线程分配CPU时间片,直到线程重新进入就绪状态(ready),才可能转到运行状态;

阻塞状态只能先进入就绪,进而由操作系统转到运行,不能直接进入运行状态;发生阻塞状态的就产生了几种情况:

1 当A线程处于运行,它试图获取同步锁时,但同步锁却被B线程获得,此时JVM会把A线程存到 共享资源的对象等待池里面,A线程进入阻塞;

2 当线程处于运行状态,它会发出了 IO请求,这时候线程会进入阻塞状态;

还有:

3 调用sleep()方法使线程进入休眠;

4 调用suspend()方法使线程进入挂起时;

5 调用wait()方法,进入等待时;

3)waiting等待状态

运行的线程会调用wait方法(是没有参数的wait方法),然后JVM会把该线程存到共享资源的对象等待池,线程就进入等待状态;处于该状态中的线程只会被其他线程唤醒的;

4)timed waiting计时等待状态:

如果运行中的线程调用带参的wait方法或者sleep方法,那么该线程是不会释放同步锁或者同步监听器的,以下两种都会进入计时等待:

  • 第一个:如果是运行中的线程,调用这个wait(long time)方法,JVM会把当前线程存到共享资源对象等待池中,那么这个线程也会进入该状态;
  • 第二个:如果当前线程执行sleep(long time)方法,也是一样的;

terminated终止状态

也可叫死亡状态,它的生命走到了尽头;线程一旦终止,就不能再重启启动了,否则会发生IllegalThreadStateException异常;

正常 执行完run方法而退出,寿终正寝,属于正常死亡;

线程执行遇到 异常而退出,线程中断,属于意外死亡;

以上两种都会让线程终止;

提示如下:

如果要测试某线程是否已经死亡,我们可以使用 isAlive()方法,如果这个方法在线程处于就绪、运行、阻塞时返回了true,新建和死亡时侯返回false。不要去试图对一个已经死亡的线程调用start()方法来重新启动,死亡就是死亡和人一样,不可能再生的。还有也不要对一个线程 调用两次start()方法,同样会引发异常;

代码示例

为了更好的理解生命周期,以及生命周期中的各个状态,及接下来:

WaitingTime

创建WaitingTime类,我们在while(true)循环中去调用TimeUnit.SECONDS.sleep(long)方法来验证线程的TIMED_WARTING状态。

show my the code

WaitingTime

WaitingState

创建这类,这个线程在一个while(true)循环中,现在想要获取当前类Class对象的synchronized锁,也就是说,这个类不管创建多少个实例,synchronized锁都是同一个,并且让这个线程会处于等待。这时候,在synchronized中用当前类的Class对象的wait()方法,目的就是验证线程的waiting状态;

show my the code

waitingState

blockedThread

它主要是在synchronized代码块中的while(true)循环中去调用我们的TimeUnit.SECONDS.sleep()来验证线程的blocked状态,当启动两个BlockedThread线程时,首先启动的线程会处于timed_waiting状态,后启动的线程就会处于blocked状态;

show my the code

ThreadState

启动各线程,验证各 线程输出的状态,如图:


运行Thread类:
第一次运行结果
现在命令行输入" jps"查看运行的进程:
2
可以看到 ThreadSate进程ID是7800,接下来,输入"jstack 7800"看下ThreadSate进程栈的信息:
4
如下:
Full thread dump OpenJDK 64-Bit Server VM (25.282-b08 mixed mode):

"DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x0000026f3717a000 nid=0x271c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"BlockedThread-02" #15 prio=5 os_prio=0 tid=0x0000026f37173800 nid=0x2440 waiting for monitor entry [0x000000aa4d3fe000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at jichu03.ThreadLive.BlockedThread.run(BlockedThread.java:15)
        - waiting to lock <0x00000000d63dd590> (a java.lang.Class for jichu03.ThreadLive.BlockedThread)
        at java.lang.Thread.run(Thread.java:748)

"BlockedThread-01" #14 prio=5 os_prio=0 tid=0x0000026f37172800 nid=0x8b8 waiting on condition [0x000000aa4d2fe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at jichu03.ThreadLive.WaitingTime.waitSecond(WaitingTime.java:21)
        at jichu03.ThreadLive.BlockedThread.run(BlockedThread.java:15)
        - locked <0x00000000d63dd590> (a java.lang.Class for jichu03.ThreadLive.BlockedThread)
        at java.lang.Thread.run(Thread.java:748)

"waitingStateThread" #13 prio=5 os_prio=0 tid=0x0000026f37170000 nid=0x16bc in Object.wait() [0x000000aa4d1ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d63d86f0> (a java.lang.Class for jichu03.ThreadLive.WaitingTime)
        at java.lang.Object.wait(Object.java:502)
        at jichu03.ThreadLive.WaitingState.run(WaitingState.java:15)
        - locked <0x00000000d63d86f0> (a java.lang.Class for jichu03.ThreadLive.WaitingTime)
        at java.lang.Thread.run(Thread.java:748)

"waitingTimeThread" #12 prio=5 os_prio=0 tid=0x0000026f3716b800 nid=0x2528 waiting on condition [0x000000aa4d0fe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
        at java.lang.Thread.sleep(Native Method)
        at java.lang.Thread.sleep(Thread.java:340)
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
        at jichu03.ThreadLive.WaitingTime.waitSecond(WaitingTime.java:21)
        at jichu03.ThreadLive.WaitingTime.run(WaitingTime.java:15)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x0000026f370b8800 nid=0x520 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x0000026f3702b000 nid=0x2e9c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000026f37022000 nid=0x1534 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000026f3701e000 nid=0x13a8 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000026f3701c000 nid=0xc58 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000026f37016000 nid=0x2f54 runnable [0x000000aa4c9fe000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
        at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
        at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
        - locked <0x00000000d64253c0> (a java.io.InputStreamReader)
        at java.io.InputStreamReader.read(InputStreamReader.java:184)
        at java.io.BufferedReader.fill(BufferedReader.java:161)
        at java.io.BufferedReader.readLine(BufferedReader.java:324)
        - locked <0x00000000d64253c0> (a java.io.InputStreamReader)
        at java.io.BufferedReader.readLine(BufferedReader.java:389)
        at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:47)

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000026f3514d000 nid=0x1568 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000026f351a3000 nid=0xad8 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x0000026f3511f800 nid=0x1024 in Object.wait() [0x000000aa4c6ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d6209508> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
        - locked <0x00000000d6209508> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000026f35118000 nid=0x26dc in Object.wait() [0x000000aa4c5ff000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x00000000d6207118> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x00000000d6207118> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x0000026f350ed800 nid=0x2674 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000026f1edc7800 nid=0x1fa8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000026f1edc9000 nid=0xb30 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000026f1edca800 nid=0x1bc0 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000026f1edcd800 nid=0x2c04 runnable

"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000026f1edcf800 nid=0x104c runnable

"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000026f1edd1000 nid=0x1550 runnable

"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000026f1edd4800 nid=0x232c runnable

"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000026f1edd5800 nid=0xb6c runnable

"VM Periodic Task Thread" os_prio=2 tid=0x0000026f370bb800 nid=0x1960 waiting on condition

JNI global references: 12

输出信息可以看出:

名称为WaitingTimeThread的线程处于TIMED_WAITING状态;名称为WaitingStateThread的线程处于WAITING状态;名称为BlockedThread-01的线程处于TIMED_WAITING状态;名称为BlockedThread-02的线程处于BLOCKED状态;

注意:使用jps结合jstack命令可以分析线上生产环境的Java进程的异常信息

也可以直接点击IDEA下图所示的图表直接打印出线程的堆栈信息;

123


公众号:《码工是小希》


流水不争先后,争的是滔滔不绝

相关文章
|
28天前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
131 0
|
14天前
|
安全
List并发线程安全问题
【10月更文挑战第21天】`List` 并发线程安全问题是多线程编程中一个非常重要的问题,需要我们认真对待和处理。只有通过不断地学习和实践,我们才能更好地掌握多线程编程的技巧和方法,提高程序的性能和稳定性。
124 59
|
10天前
|
Java API 调度
Java 线程的生命周期
在JDK 1.5之前,线程的生命周期包括五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)。JDK 1.5及之后增加了三种阻塞状态,共六种状态:新建、可运行、终止、锁阻塞、计时等待和无限等待。这些状态描述了线程在操作系统和JVM中的不同阶段。
Java 线程的生命周期
|
14天前
|
Java 调度
[Java]线程生命周期与线程通信
本文详细探讨了线程生命周期与线程通信。文章首先分析了线程的五个基本状态及其转换过程,结合JDK1.8版本的特点进行了深入讲解。接着,通过多个实例介绍了线程通信的几种实现方式,包括使用`volatile`关键字、`Object`类的`wait()`和`notify()`方法、`CountDownLatch`、`ReentrantLock`结合`Condition`以及`LockSupport`等工具。全文旨在帮助读者理解线程管理的核心概念和技术细节。
33 1
[Java]线程生命周期与线程通信
|
5天前
|
存储 设计模式 分布式计算
Java中的多线程编程:并发与并行的深度解析####
在当今软件开发领域,多线程编程已成为提升应用性能、响应速度及资源利用率的关键手段之一。本文将深入探讨Java平台上的多线程机制,从基础概念到高级应用,全面解析并发与并行编程的核心理念、实现方式及其在实际项目中的应用策略。不同于常规摘要的简洁概述,本文旨在通过详尽的技术剖析,为读者构建一个系统化的多线程知识框架,辅以生动实例,让抽象概念具体化,复杂问题简单化。 ####
|
26天前
|
Java
【编程进阶知识】揭秘Java多线程:并发与顺序编程的奥秘
本文介绍了Java多线程编程的基础,通过对比顺序执行和并发执行的方式,展示了如何使用`run`方法和`start`方法来控制线程的执行模式。文章通过具体示例详细解析了两者的异同及应用场景,帮助读者更好地理解和运用多线程技术。
25 1
|
1月前
|
Java 调度
Java一个线程的生命周期详解
Java中,一个线程的生命周期分为五个阶段:NEW(新建),RUNNABLE(可运行),BLOCKED(阻塞),WAITING(等待),TERMINATED(终止)。线程创建后处于新建状态,调用start方法进入可运行状态,执行中可能因等待资源进入阻塞或等待状态,正常完成或异常终止后进入终止状态。各状态间可相互转换,构成线程的生命周期。
|
2月前
|
网络协议 C语言
C语言 网络编程(十四)并发的TCP服务端-以线程完成功能
这段代码实现了一个基于TCP协议的多线程服务器和客户端程序,服务器端通过为每个客户端创建独立的线程来处理并发请求,解决了粘包问题并支持不定长数据传输。服务器监听在IP地址`172.17.140.183`的`8080`端口上,接收客户端发来的数据,并将接收到的消息添加“-回传”后返回给客户端。客户端则可以循环输入并发送数据,同时接收服务器回传的信息。当输入“exit”时,客户端会结束与服务器的通信并关闭连接。
|
2月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
56 0
|
2月前
|
C语言
C语言 网络编程(九)并发的UDP服务端 以线程完成功能
这是一个基于UDP协议的客户端和服务端程序,其中服务端采用多线程并发处理客户端请求。客户端通过UDP向服务端发送登录请求,并根据登录结果与服务端的新子线程进行后续交互。服务端在主线程中接收客户端请求并创建新线程处理登录验证及后续通信,子线程创建新的套接字并与客户端进行数据交换。该程序展示了如何利用线程和UDP实现简单的并发服务器架构。