Java并发编程的魅力之线程的几种状态
1.1 线程的状态
上面的程序如果你细心观察,你会发现,我不仅打印了线程的ID,名称还打印了线程的执行状态。
Java线程在运行的生命周期中可能处于下表中所示的6种不同的状态,
在给定的一个时刻,线程只能处于其中的一个状态。
状态名称 | 说明 |
---|---|
NEW | 初始状态,线程被构建,但是还没有调用start()方法 |
RUNNABLE | 运行状态,Java 线程将操作系统中的就绪和运行两种状态统称为运行中。 |
BLOCKED | 阻塞状态,表示线程阻塞于锁 |
WAITING | 等待状态,表示线程处于等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断) |
TIME_WAITING | 超时等待状态,该状态不同于WAITING,它是可以在指定的时间自行返回的。 |
TERMINATED | 终止状态,表示当前线程已经执行完毕 |
1.2 线程状态示例
接下来我们写一个示例来模拟下
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.TimeUnit;
/**
* @author qing-feng.zhao
* @功能 线程的状态
* @时间 2019/12/19 14:58
*/
public class ThreadState {
public static void main(String[] args) {
//模拟一个TIMED_WAITING 状态的线程 一直休眠状态的线程
new Thread(new TimeWaiting(),"TimeWaitingThread").start();
//模拟一个WAITING 状态的线程 一直等待状态线程
new Thread(new Waiting(),"WaitingHead").start();
//模拟两个线程,一个处于BLOCKED状态的线程,一个处于TIMED_WAITING状态的线程
new Thread(new Blocked(),"BlockedThread-1").start();
new Thread(new Blocked(),"BlockedThread-2").start();
//显示线程的执行状态
showThreadInfo();
}
/**
* 该线程不断地进行睡眠,比如懒洋洋
*/
static class TimeWaiting implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
System.err.println("休眠出错:"+e);
}
}
}
}
/**
* 该线程不断地进行等待
* 锁住当前Waiting.class
*/
static class Waiting implements Runnable{
@Override
public void run() {
//一直等待
while(true){
synchronized (Waiting.class){
try {
Waiting.class.wait();
} catch (InterruptedException e) {
System.err.println("线程等待异常:"+e);
}
}
}
}
}
/**
* 该线程在Blocked.class 实例上加锁后,不会释放锁
*/
static class Blocked implements Runnable{
@Override
public void run() {
//获取锁
synchronized (Blocked.class){
//一直睡觉不释放锁
while (true){
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
System.err.println("休眠出错:"+e);
}
}
}
}
}
private static void showThreadInfo(){
//获取Java线程管理MXBean
ThreadMXBean threadMXBean= ManagementFactory.getThreadMXBean();
//不需要获取同步的monitor和 synchronized 信息,仅获取线程和线程堆栈信息
ThreadInfo[] threadInfoArray=threadMXBean.dumpAllThreads(true,true);
//遍历线程信息,仅打印线程ID 和线程名称信息
for (ThreadInfo threadInfo:threadInfoArray
) {
System.out.println("["+threadInfo.getThreadId()+"]"+threadInfo.getThreadName()+"["+threadInfo.getThreadState()+"]");
}
}
}
输出结果如下:
[14]BlockedThread-2[BLOCKED]
[13]BlockedThread-1[TIMED_WAITING]
[12]WaitingHead[WAITING]
[11]TimeWaitingThread[TIMED_WAITING]
[6]Monitor Ctrl-Break[RUNNABLE]
[5]Attach Listener[RUNNABLE]
[4]Signal Dispatcher[RUNNABLE]
[3]Finalizer[WAITING]
[2]Reference Handler[WAITING]
[1]main[RUNNABLE]
当然还有一种方法,我们输入如下命令,可查看当前运行的java 程序进程
jps -l
输出内容如下所示:
8080 sun.tools.jps.jps 1848 com.xingyun.main.ThreadState
我们可以得出当前程序的进程ID 是1848,
输入如下命令查看详细信息
jstack 1848
输出内容如下:
mixed mode): "DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002398000 nid=0x2468 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "BlockedThread-2" #14 prio=5 os_prio=0 tid=0x0000000058c5d000 nid=0x2540 waiting for monitor entry [0x000000005a34f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.xingyun.main.ThreadState$Blocked.run(ThreadState.java:89) - waiting to lock <0x00000000d57263c8> (a java.lang.Class for com.xingyun.main.ThreadState$Blocked) at java.lang.Thread.run(Thread.java:748) "BlockedThread-1" #13 prio=5 os_prio=0 tid=0x0000000058c5a000 nid=0x24e4 waiting on condition [0x000000005a15f000] 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 com.xingyun.main.ThreadState$Blocked.run(ThreadState.java:89) - locked <0x00000000d57263c8> (a java.lang.Class for com.xingyun.main.ThreadState$Blocked) at java.lang.Thread.run(Thread.java:748) "WaitingHead" #12 prio=5 os_prio=0 tid=0x0000000058c52000 nid=0x1c90 in Object.wait() [0x0000000059f2f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5722c28> (a java.lang.Class for com.xingyun.main.ThreadState$Waiting) at java.lang.Object.wait(Object.java:502) at com.xingyun.main.ThreadState$Waiting.run(ThreadState.java:68) - locked <0x00000000d5722c28> (a java.lang.Class for com.xingyun.main.ThreadState$Waiting) at java.lang.Thread.run(Thread.java:748) "TimeWaitingThread" #11 prio=5 os_prio=0 tid=0x0000000058c51800 nid=0x27e0 waiting on condition [0x0000000059c2e000] 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 com.xingyun.main.ThreadState$TimeWaiting.run(ThreadState.java:48) at java.lang.Thread.run(Thread.java:748) "Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000058b74000 nid=0x2524 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000058ae6000 nid=0x1804 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000058ac9000 nid=0x269c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000058a9d000 nid=0x2568 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000058add000 nid=0x1514 runnable [0x000000005913e000] 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 <0x00000000d57b3d28> (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 <0x00000000d57b3d28> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000058a7b000 nid=0x2448 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000005762a800 nid=0x2650 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000005760a800 nid=0x2620 in Object.wait() [0x000000005896f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5588ec8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked <0x00000000d5588ec8> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x00000000575c3800 nid=0x1db4 in Object.wait() [0x0000000058a6f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000d5586b68> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x00000000d5586b68> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x00000000575bb800 nid=0x2134 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000023ad800 nid=0x275c runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000023af000 nid=0x728 runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000023b0800 nid=0x1ee4 runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000023b2000 nid=0x1dbc runnable "VM Periodic Task Thread" os_prio=2 tid=0x0000000058ba4000 nid=0x3c4 waiting on condition JNI global references: 33
通过示例,我们了解到Java程序运行中线程状态的具体含义。
- 线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换
- Java线程状态变化如下:
线程创建之后,调用start()方法开始运行。当线程执行wait()方法之后,线程进入等待状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而超时等待状态相当于在等待状态的基础上增加了超时限制,也就是超时时间到达时将会返回到运行状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到阻塞状态。线程在执行Runnable的run()方法之后将会进入到终止状态。
注意:
- Java将操作系统中的运行和就绪两个状态合并称为运行状态。
- 阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态,但是阻塞在java. concurrent包中Lock接口的线程状态却是等待状态,因为
java.concurrent
包中Lock接口对于阻塞的实现均使用了LockSupport类中的相关方法。