• JVisualVM 中线程状态(运行/休眠/等待/驻留/监视)解析


    在java自带的工具JVirtualVM中线程有以下几种状态:

    先说结论,各状态含义如下,后面有详细的demo测试验证:

    • 运行(runnable):正在运行中的线程。

    • 休眠(timed_waiting):休眠线程,例如调用Thread.sleep方法。

    • 等待(waiting):等待唤醒的线程,可通过调用Object.wait方法获得这种状态,底层实现是基于对象头中的monitor对象

    • 驻留(waiting):等待唤醒的线程,和等待状态类似,只不过底层的实现方式不同,处于这种状态的例子有线程池中的空闲线程,等待获取reentrantLock锁的线程,调用了reentrantLock的condition的await方法的线程等等,底层实现是基于Unsafe类的park方法,在AQS中有大量的应用。

    • 监视(blocked):等待获取monitor锁的线程,例如等待进入synchronize代码块的线程。

    代码测试

    1. private static final ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); private static final ReentrantLock reentrantLockTest = new ReentrantLock(); public static void main(String[] args) { //基于println方法中的synchronize代码块测试运行或者监视线程
    2. Thread thread1 = new Thread(() -> { while (true) {
    3. System.out.println("运行或者监视线程1");
    4. }
    5. }, "运行或者监视线程1");
    6. thread1.start(); //基于println方法中的synchronize代码块测试运行或者监视线程
    7. Thread thread2 = new Thread(() -> { while (true) {
    8. System.out.println("运行或者监视线程2");
    9. }
    10. }, "运行或者监视线程2");
    11. thread2.start(); //monitor对象等待线程
    12. Object lock = new Object(); Thread thread3 = new Thread(() -> { synchronized (lock) { try {
    13. lock.wait();
    14. } catch (InterruptedException e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. }, "等待线程synchronized");
    19. thread3.start(); //reentrantLock中的条件对象调用await方法线程为驻留线程
    20. ReentrantLock reentrantLock = new ReentrantLock(); Condition condition = reentrantLock.newCondition(); Thread thread4 = new Thread(() -> {
    21. reentrantLock.lock(); try {
    22. condition.await();
    23. } catch (InterruptedException e) {
    24. e.printStackTrace();
    25. } finally {
    26. reentrantLock.unlock();
    27. }
    28. }, "等待线程reentrantLock");
    29. thread4.start(); //休眠线程
    30. Thread thread5 = new Thread(() -> { try {
    31. Thread.sleep(1000000);
    32. } catch (InterruptedException e) {
    33. e.printStackTrace();
    34. }
    35. }, "休眠线程");
    36. thread5.start(); Thread thread6 = new Thread(ThreadTest::lockMethod, "reentrantLock运行线程");
    37. thread6.start(); //等待获取reentrantLock的线程为驻留线程
    38. Thread thread7 = new Thread(() -> { try {
    39. TimeUnit.MICROSECONDS.sleep(10);
    40. } catch (InterruptedException e) {
    41. e.printStackTrace();
    42. }
    43. lockMethod();
    44. }, "reentrantLock监视线程");
    45. thread7.start(); //线程池中的空闲线程为驻留线程
    46. singleThreadExecutor.execute(() -> { //线程池中的线程是懒加载,需要运行任务之后才会产生线程
    47. System.out.println("驻留线程运行");
    48. });
    49. } private static void lockMethod() {
    50. reentrantLockTest.lock(); try { while (true) {
    51. }
    52. } finally {
    53. reentrantLockTest.unlock();
    54. }
    55. } //println源码也简单贴一下
    56. //java.io.PrintStream#println(java.lang.String)
    57. public void println(String x) { //this表示System.out这个PrintStream对象
    58. synchronized (this) {
    59. print(x);
    60. newLine();
    61. }
    62. }

    以上代码运行之后,打开JVirtualVM查看线程如下:

    根据代码的顺序从上至下讲:

    • 两个运行或者监视线程的System.out.println()语句因为抢同一个synchronize锁,可以很明显的看出是在交替运行,状态分别处于运行监视状态。

    • 等待线程synchronize因为调用了Object的wait方法,一直处于等待状态。

    • 休眠线程省略。

    • 重点是和reentrantLock锁相关的三个线程,注意下上图中有一个地方写错了,等待线程reentrantLock,实际应该是驻留线程reentrantLock,可以看到无论是通过condition的await方法,还是在阻塞等待锁的过程中,都是处于驻留状态,而不是我一开始预想的等待状态,通过查看源码后发现它们最终都调用了Unsafe的park方法,后续的线程dump也能验证这一点。

    • pool- 1-threadpool-1就是那个线程池,因为里面的线程处于空闲状态,也属于驻留

    线程dump

  • 相关阅读:
    C++面试经典题目
    程序员就该这样解读《隐秘的角落》
    AI与制药相结合,能否弯道超车
    SpringBootAdmin环境搭建与使用
    1.Qt5.15及其以上的下载
    (单表单条数据,单表多条数据,多表单条数据,多表多条数据)查询
    avue的操作,搜索,table列的自定义配置
    5.MySQL高级语句,你给我学!
    AD7793驱动程序(详细)
    Spring Boot + Vue3前后端分离实战wiki知识库系统<十三>--单点登录开发二
  • 原文地址:https://blog.csdn.net/m0_73311735/article/details/126688725