Java线程的6种状态和JVM状态打印
原文网址:Java线程的6种状态和JVM状态打印-CSDN博客
简介
本文介绍Java线程的6种状态和JVM状态。
Java线程的6种状态
1. NEW:初始状态
线程被创建(比如new Thread()),但还没调用start()方法。
2. RUNNABLE:运行状态
这两种都是运行状态:就绪或正在运行。调用start()后进入此状态。
3. BLOCKED:阻塞状态
表示线程在等“锁”(监视器锁(monitor lock)),比如synchronized锁。
4. WAITING:无限等待状态
等其他线程通知或中断,才能执行。
以下方法调用后会进入此状态:
- 调用 Object.wait() 且不带超时
- 调用 Thread.join() 且不带超时
- 调用 LockSupport.park()
5. TIMED_WAITING:限时等待
在指定时间内等待另其他线程执行特定操作。
以下方法调用后会进入此状态:
- 调用 Thread.sleep(long)
- 调用 Object.wait(long)
- 调用 Thread.join(long)
- 调用 LockSupport.parkNanos(long) / parkUntil(long)
6. TERMINATED:终止状态
线程执行结束(成功执行或异常结束)。
Java线程状态切换
1. NEW-> RUNNABLE
状态描述:当使用 new Thread() 创建一个线程对象后,该线程就处于 NEW 状态,此时,它仅仅是一个Java对象,JVM尚未为其分配任何系统资源,也未与操作系统底层的线程关联。
触发状态转换:调用线程对象的 start() 方法
细节:调用后,JVM会向操作系统申请创建一个对应的原生线程,并将其纳入操作系统的线程调度器管理,线程状态变为 RUNNABLE。进入RUNNABLE并不意味着立刻执行,只是表示“可以被CPU执行了”,具体何时执行取决于操作系统的调度。
2. RUNNABLE <-> BLOCKED
状态描述:BLOCKED 状态与 synchronized 关键字(即监视器锁 Monitor)强相关。
RUNNABLE -> BLOCKED
触发状态转换:线程尝试进入一个 synchronized 修饰的同步代码块或方法,但该代码块的监视器锁已被其他线程持有。
细节:获取锁失败的线程会进入该锁的阻塞队列 (Entry Set),状态变为 BLOCKED,处于此状态的线程不会占用CPU时间。
BLOCKED -> RUNNABLE
触发状态转换:持有该监视器锁的线程释放了锁(即退出了同步代码块)。
细节:操作系统会从该锁的阻塞队列中,按照一定规则(可能是公平或非公平)唤醒一个或多个 BLOCKED 状态的线程,让它们重新去竞争锁,被唤醒的线程状态变回 RUNNABLE,但只有成功竞争到锁的线程才能执行。
3. RUNNABLE <-> WAITING / TIMED_WAITING
这两个状态都表示线程在等待,区别在于有无时间限制
RUNNABLE -> WAITING
触发状态转换:线程在持有锁的情况下,调用了不带超时的 Object.wait()、Thread.join() 或 LockSupport.park()。
Object.wait() 细节(这是典型场景):线程必须先持有对象的监视器锁,才能调用该对象的 wait() 方法,调用后,线程会立即释放持有的锁,并进入该对象的等待集合 (Wait Set),状态变为 WAITING,此状态同样不占用CPU。
RUNNABLE -> TIMED_WAITING
触发状态转换:与 WAITING 类似,但调用的是带超时参数的方法,如 Thread.sleep(long)、Object.wait(long)、Thread.join(long) 等。
Thread.sleep(long) 细节(这是特殊情况):调用 sleep 方法会让线程进入 TIMED_WAITING 状态,但它不释放它已持有的锁,这是 sleep 和 wait 的一个核心区别。
Object.wait(long) 细节:与 wait() 类似,调用后会释放锁并进入等待集合,但它有一个最长等待时间。
WAITING / TIMED_WAITING -> RUNNABLE / BLOCKED
触发状态转换:对于 WAITING 状态的线程(由 wait() 导致),等待另一个线程在同一个对象上调用 Object.notify() 或 Object.notifyAll()。
TIMED_WAITING 状态的线程,除了上述 notify/notifyAll 唤醒外,等待时间超时也会自动唤醒。
【面试重点细节】:被唤醒的线程不会立刻恢复到 RUNNABLE 状态,它会从等待集合 (Wait Set) 移动到阻塞队列 (Entry Set),状态变为 BLOCKED,然后重新去竞争锁,只有当它再次成功获取到锁之后,才会从 BLOCKED 状态转换回 RUNNABLE 状态继续执行。
4. RUNNABLE -> TERMINATED
状态描述:线程的使命终结。
触发状态转换:
- 线程的 run() 方法正常执行完毕,自然退出
- run() 方法因未捕获的异常而意外终止
细节:一旦线程进入 TERMINATED 状态,它的生命周期就结束了,不能再通过调用 start() 方法使其复活,此时,与操作系统底层的线程关联也会被取消。
JVM状态打印
状态大全
Deadlock
表示有死锁
Waiting on condition
含义:它在等待其他资源把自己唤醒,或者是它是调用了 sleep(N)。
此时状态:WAITING || TIMED_WAITING。
举例:
java.lang.Thread.State: WAITING (parking):一直等某个条件发生
java.lang.Thread.State: TIMED_WAITING (parking或sleeping):设有超时时间,那个条件不到来,也将定时唤醒自己。
如果大量线程在“waiting on condition”,可能是它们又跑去获取第三方资源,尤其是第三方网络资源,迟迟获取不到Response,导致大量线程进入等待状态。
waiting for monitor entry
含义:它在等待进入一个临界区 ,所以它在”Entry Set“队列中等待。
此时状态:BLOCKED
举例:java.lang.Thread.State: BLOCKED (on object monitor)
知识点:注意 “Entry Set” 就是咱们平时经常使用synchronized 的时候线程所等待的区域。
如果大量线程在 “waiting for monitor entry”,可能是一个全局锁阻塞住了大量线程。如果短时间内多次打印的 thread dump 文件反映,随着时间流逝,waiting for monitor entry 的线程越来越多,没有减少的趋势,可能意味着某些线程在临界区里呆的时间太长了,以至于越来越多新线程迟迟无法进入临界区。
in Object.wait()
含义:说明它获得了监视器锁之后(也就是进入synchronized方法块),又调用了 java.lang.Object.wait() 方法。
每个 Monitor在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set”和 “Wait Set”里面等候。在 “Entry Set”中等待的线程状态是 “Waiting for monitor entry”,而在 “Wait Set”中等待的线程状态是 “in Object.wait()”。
当线程获得了 Monitor,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃了 Monitor,进入 “Wait Set”队列。
此时状态:TIMED_WAITING || WAITING
举例:
java.lang.Thread.State: TIMED_WAITING (on object monitor);
java.lang.Thread.State: WAITING (on object monitor);
查看状态的方法
可以打印出JVM里线程的状态,方法有以下几种:
- jstack
- jconsole
- VisualVM
以上方法都能做到:展示每个线程的状态(比如BLOCKED (on object monitor)、WAITING (parking)等),还能看到线程在等什么锁、在哪行代码阻塞。
比如:
“RMI TCP Connection(idle)” daemon prio=10 tid=0x00007fd50834e800 nid=0x56b2 waiting on condition [0x00007fd4f1a59000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
parking to wait for <0x00000000acd84de8> (a java.util.concurrent.SynchronousQueueTransferStack)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:198)
at java.util.concurrent.SynchronousQueueTransferStack.awaitFulfill(SynchronousQueue.java:424)atjava.util.concurrent.SynchronousQueueTransferStack.awaitFulfill(SynchronousQueue.java:424) at java.util.concurrent.SynchronousQueueTransferStack.awaitFulfill(SynchronousQueue.java:424)atjava.util.concurrent.SynchronousQueueTransferStack.transfer(SynchronousQueue.java:323)
at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:874)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:945)
at java.util.concurrent.ThreadPoolExecutorWorker.run(ThreadPoolExecutor.java:907)
at java.lang.Thread.run(Thread.java:662)