• 线程状态场景模拟以及问题定位


    前言

      在看skywalking-agent源码时发现agent会采集线程的状态,但这些状态是什么场景触发的呢?当线程数超过阈值后,我们怎样去更快地定位问题呢?带上这样的疑问进行下面的分析
    在这里插入图片描述

    线程状态分析

    1. 在java.lang.Thread类的内部枚举类State中定义了Java线程的六个状态

       public enum State {
          /**
           * Thread state for a thread which has not yet started.
           */
          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.
           */
          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}.
           */
          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. */ 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}
      • *
      */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
    2. 在操作系统层面,线程存在五类状态(状态流转如下图)
      在这里插入图片描述
      2.1 Java中的RUNNABLE包含了OS中的RUNNINGREADY
      2.2 Java中的WAITINGTIMED_WAITINGBLOCKED对应OS中的WAITING

    3. 下面按照java.lang.Thread$State中对状态的描述场景去模拟测试,主要分析WAITINGTIMED_WAITINGBLOCKED

    BLOCKED状态模拟

    1. BLOCKED状态注释翻译:线程阻塞等待监视器锁的线程状态。 处于阻塞状态的线程正在等待监视器锁进入同步块/方法或调用 Object.wait 后重新进入同步块/方法
    2. 场景1:处于阻塞状态的线程正在等待监视器锁进入同步块/方法
      2.1 模拟代码如下
      @Test
      public void testSynchronizedWaitBlock(){
          Object monitor = new Object();
          Thread thread1 = new Thread(()->{
              synchronized (monitor){
                  try {
                      monitor.wait();
                  } catch (InterruptedException e) {
                      System.out.println("捕获到InterruptedException");
                      throw new RuntimeException(e);
                  }
                  System.out.println("执行thread1方法");
              }
          },"thread1");
      
          Thread thread2 = new Thread(()->{
              synchronized (monitor){
                  monitor.notifyAll();
                  System.out.println("执行thread2方法");
              }
          },"thread2");
          thread1.start();
          thread2.start();
      
          System.out.println("断点暂停主线程");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      2.2 thread1执行monitor.wait()前,thread2因要等待监视器所有权BLOCKED住了
      在这里插入图片描述
    3. 场景2:调用Object.wait 后重新进入同步块/方法
      3.1 thread1执行monitor.wait()后并且thread2执行为monitor.notifyAll()前,thread1释放监视器所有权并等待,线程状态为WAITING。thread2的状态为RUNNABLE
      在这里插入图片描述
      3.2 thread2执行为monitor.notifyAll()后,thread1等待获取监视器的所有权并恢复执行,状态变为BLOCKED
      在这里插入图片描述

    WAITING状态模拟

    1. WAITING状态注释翻译:处于等待状态的线程正在等待另一个线程执行特定操作,调用以下方法之一,线程处于等待状态:
      1.1 没有超时参数的Object.wait
      1.2 没有超时参数的Thread.join
      1.3 LockSupport.park

    2. 场景1:没有超时参数的Thread.join
      2.1 模拟代码如下

      // Thread.join 等待这个线程死掉
      @Test
      public void testJoin(){
          Thread thread1 = new Thread(()->{
              System.out.println("执行thread1方法.....");
      
          },"thread1");
      
          Thread thread2 = new Thread(()->{
              try {
                  thread1.join();
              } catch (InterruptedException e) {
                  throw new RuntimeException(e);
              }
              System.out.println("执行thread2方法.....");
      
          },"thread2");
      
          thread1.start();
          thread2.start();
      
          System.out.println("断点暂停主线程");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23

      2.2 thread2执行thread1.join等待thread1执行完,此时thread2的状态为WAITING
      在这里插入图片描述

    3. 场景2:LockSupport.park
      3.1 模拟代码如下

      /**
       * 除非许可可用,否则禁用当前线程以进行线程调度
       *  --> 当前线程将出于线程调度目的而被禁用并处于休眠状态,直到发生以下三种情况之一:
       *  1. 其他一些线程以当前线程为目标调用unpark
       *  2. 其他一些线程中断当前线程
       * @throws Exception
       */
      @Test
      public void testLockSupport(){
          Thread thread1 = new Thread(() -> {
              System.out.println("do something start");
              LockSupport.park();
              System.out.println("do something end");
          },"thread1");
      
          thread1.start();
      
          System.out.println("给子线程thread增加一个许可");
          LockSupport.unpark(thread1);
          System.out.println("主线程执行完毕");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

      3.2 thread1调用LockSupport.park()将当前线程thread1处于休眠状态,状态为WAITING
      在这里插入图片描述
      3.3 主线程调用LockSupport.unpark(thread1)将thread1唤醒,thread1继续执行,状态为RUNNABLE

    TIME_WAITING状态模拟

    1. TIME_WAITING状态注释翻译:具有指定等待时间的等待线程的线程状态,调用以下方法之一,线程处于定时等待状态:
      1.1 Thread.sleep
      1.2 带有超时参数的Object.wait
      1.3 带有超时参数的Thread.join
      1.4 LockSupport.parkNanos
      1.5 LockSupport.parkUntil
    2. 场景1:Thread.sleep
      2.1 模拟代码如下:
        /**
       * 使当前执行的线程休眠(暂时停止执行)指定的毫秒数,
       * 取决于系统计时器和调度程序的精度和准确性。 该线程不会失去任何监视器的所有权
       */
      @Test
      public void testSleep(){
          Object monitor = new Object();
          Thread thread1 = new Thread(()->{
              synchronized (monitor){
                  try {
                      Thread.sleep(1000 * 10);
                  } catch (InterruptedException e) {
                      System.out.println("捕获到InterruptedException");
                      throw new RuntimeException(e);
                  }
                  System.out.println("执行thread1方法");
              }
          },"thread1");
      
          Thread thread2 = new Thread(()->{
              synchronized (monitor){
                  System.out.println("执行thread2方法");
              }
          },"thread2");
          thread1.start();
          thread2.start();
      
          System.out.println("断点暂停主线程");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      2.2 thread1获取monitor监视器的所有权,调用sleep使当前线程休眠,但不会丢失监视器的所有权,所以thread1的状态为TIMED_WAITING,thread2的状态为BLOCKED
      在这里插入图片描述

    线程数预警后如何排查问题

    1. 线程最大数量由JVM的堆(-Xmx,-Xms)大小、Thread的栈(-Xss)内存大小、系统最大可创建的线程数的限制参数三个方面影响。不考虑系统限制,可以通过这个公式估算:
      线程数量 = (机器本身可用内存 - JVM分配的堆内存) / Xss的值
    2. 当线程数超过阈值后,通过arthas的thread -b命令找出当前阻塞其他线程的线程
      在这里插入图片描述

    束语

      个人认为统计线程信息是为监控系统健康状况,当线程数达到预设阈值后,应该将当时线程堆栈信息打印出来保存起来(维护现场)并及时进行告警通知,后续更好的定位问题。
    (在skywalking中没有找到这样的设置,希望大佬指点~~~~)

  • 相关阅读:
    keepalived群集
    两道相似的三维dp动态规划题
    相机数据恢复!详细步骤解析(2023新版)
    【云原生 · Kubernetes】KubeVirt热迁移
    摄影网页设计制作 简单静态HTML网页作品 WEB静态摄影网站作业成品 学生DW摄影网站模板
    科学计算库 —— Matplotlib
    Two-Stream Consensus Network论文阅读
    7-39 计算阶乘和
    php敏感词汇替换,或者返回触及了哪个敏感词
    Ubuntu查看端口状态
  • 原文地址:https://blog.csdn.net/weixin_40803011/article/details/126875498