【線程】ThreadGroup 實戰與剖析 (十七)

我的原則:先會用再說,內部慢慢來。
學以致用,根據場景學源碼


一、前言

1.1 架構

在這裏插入圖片描述

在這裏插入圖片描述
=== 點擊查看top目錄 ===

1.2 ThreadGroup 能幹嘛?

  1. 能用來操作group下面的所有線程,比如全部打斷 interrupt
  2. 可以進行鏈路監控,例如監控某個 group 下面,當前存在多少活躍線程數目。
  3. 統一捕獲該group下線程拋出的異常。

1.3 ThreadGroup 常用的方法

方法 描述
int activeCount() 查看組內部 thread 活躍數量(包括子group)
int activeGroupCount() 查看組內部 group 活躍數量(包括子group)
void list() 打印出group下的所有線程信息(包括子group)
void destroy() 摧毀線程(包括子group)
boolean isDestroyed() 查看該 group 有沒有被摧毀
int enumerate(Thread[] list) copy 某個group 下面的活躍 thread
ThreadGroup getParent() 找爸爸
void interrupt() 打斷這個 group 下面的所有 thread
boolean isDaemon() 看下是否是幽靈線程
void setDaemon(boolean daemon) 設置幽靈線程

二、實戰

2.1 實戰一 :驗證 Count

  • 本demo驗證了activeCount、activeGroupCount、list、getParent、interrupt 等一系列方法
  • 代碼流程如下:
  1. 創建 group1
  2. group1 先創建thread1,2,3 ,由於沒start,activeCount得到的的結果是 0
  3. thread1,2,3 啓動起來(暫時不關閉),activeCount得到的的結果是 3
  4. 插入 thread4, 啓動起來,activeCount得到的的結果是 4
  5. thread4 線程跑完,activeCount得到的的結果是 3
  6. 創建group2(傳入 group1)
  7. 打印出 group2,group1,還有頂級 main 的list信息
  8. interrupt 測試打斷,誒,雖然main線程跑到最後了,打那是發現scanner.nextLine() 由於內部的死循環while,無法被打斷。
public static void testGroupListAndSize() throws Exception {
        ThreadGroup group1 = new ThreadGroup("Group1");

        Thread t1 = new Thread(group1, () -> {
            try {
                Scanner sc = new Scanner(System.in);
                System.out.println("點擊任意鍵喚醒線程 ...");
                sc.nextLine();
            } catch (Exception e) {
                System.out.println("t1 被打斷啦 ...");
            }
        });
        Thread t2 = new Thread(group1, () -> {
            try {
                Scanner sc = new Scanner(System.in);
                System.out.println("點擊任意鍵喚醒線程 ...");
                sc.nextLine();
            } catch (Exception e) {
                System.out.println("t2 被打斷啦 ...");
            }

        });
        Thread t3 = new Thread(group1, () -> {
            try {
                Scanner sc = new Scanner(System.in);
                System.out.println("點擊任意鍵喚醒線程 ...");
                sc.nextLine();
            } catch (Exception e) {
                System.out.println("t3 被打斷啦 ...");
            }
        });
        /*
            1. 線程未啓動,未註冊到 group 裏面去
         */
        group1.list();
        System.out.println("group1.size ->  " + group1.activeCount());

        Thread.sleep(1000);
        System.out.println("----");

        /*
            2. 啓動線程
         */
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        group1.list();
        System.out.println("group1.size ->  " + group1.activeCount());
        System.out.println("----");
        System.out.println("==== 啓動 thread4,驗證 activeCount 含義(只返回活躍數量) ====");

        /*
            啓動 thread4,驗證 activeCount 含義(只返回活躍數量)
         */
        Thread t4 = new Thread(group1, () -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.out.println("t4 被打斷啦 ...");
            }
        });
        t4.start();
        group1.list();
        System.out.println("group1.size ->  " + group1.activeCount());
        System.out.println("----");
        Thread.sleep(2000);
        System.out.println(" === 2s 後 ,t4 已經down ===");
        group1.list();
        System.out.println("group1.size ->  " + group1.activeCount());
        System.out.println("----");

        Thread.sleep(1000);
        System.out.println("==== 創建子group ====");
        ThreadGroup group2 = new ThreadGroup(group1,"Group2");
        Thread t21 = new Thread(group2, () -> {
            try {
                Scanner sc = new Scanner(System.in);
                System.out.println("點擊任意鍵喚醒線程 ...");
                sc.nextLine();
            } catch (Exception e) {
                System.out.println("t21 被打斷啦 ...");
            }
        });
        t21.start();
        Thread.sleep(1000);
        System.out.println("遍歷下 group1 ,觀察是否加入了...");
        group1.list();
        System.out.println("遍歷下 group2 ...");
        group2.list();
        System.out.println("最後遍歷下最牛逼的 group-main ...");
        Thread.currentThread().getThreadGroup().list();

        Thread.sleep(1000);
        /*
            interrupt
         */
        System.out.println("=== 最後打斷全部線程 ===");
//        group1.destroy();
        group1.interrupt();
        /*
            sc.nextLine(); 太牛逼了,裏面有死循環,聽不到外面的打斷
         */
        System.out.println("main thread end ...");
    }
  • 輸出:
java.lang.ThreadGroup[name=Group1,maxpri=10]
group1.size ->  0
----
點擊任意鍵喚醒線程 ...
點擊任意鍵喚醒線程 ...
點擊任意鍵喚醒線程 ...
java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
group1.size ->  3
----
==== 啓動 thread4,驗證 activeCount 含義(只返回活躍數量) ====
java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
    Thread[Thread-3,5,Group1]
group1.size ->  4
----
 === 2s 後 ,t4 已經down ===
java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
group1.size ->  3
----
==== 創建子group ====
點擊任意鍵喚醒線程 ...
遍歷下 group1 ,觀察是否加入了...
java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
    java.lang.ThreadGroup[name=Group2,maxpri=10]
        Thread[Thread-4,5,Group2]
遍歷下 group2 ...
java.lang.ThreadGroup[name=Group2,maxpri=10]
    Thread[Thread-4,5,Group2]
最後遍歷下最牛逼的 group-main ...
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    Thread[Monitor Ctrl-Break,5,main]
    java.lang.ThreadGroup[name=Group1,maxpri=10]
        Thread[Thread-0,5,Group1]
        Thread[Thread-1,5,Group1]
        Thread[Thread-2,5,Group1]
        java.lang.ThreadGroup[name=Group2,maxpri=10]
            Thread[Thread-4,5,Group2]
=== 最後打斷全部線程 ===
main thread end ...

...
注意,這個地方子線程沒有中斷!!!一直跑着!!!

=== 點擊查看top目錄 ===

2.2 實戰二 :驗證 Interrupt

  • 注意,上面這個程序,子線程沒有中斷!!!一直跑着!!!
  • 由於上面有bug,我們重新驗證下 interrupt 方法
Thread t1 = new Thread(group1, () -> {
     try {
         Thread.sleep(10000);
     } catch (Exception e) {
         System.out.println("t1 被打斷啦 ...");
     }
 });
  • 改動一下線程的內部實現,其餘地方簡化一下
public static void testInterrupte() throws Exception {
        ThreadGroup group1 = new ThreadGroup("Group1");

        Thread t1 = new Thread(group1, () -> {
            try {
                Thread.sleep(10000);
            } catch (Exception e) {
                System.out.println("t1 被打斷啦 ...");
            }
        });
        Thread t2 = new Thread(group1, () -> {
            try {
                Thread.sleep(10000);
            } catch (Exception e) {
                System.out.println("t2 被打斷啦 ...");
            }

        });
        Thread t3 = new Thread(group1, () -> {
            try {
                Thread.sleep(10000);
            } catch (Exception e) {
                System.out.println("t3 被打斷啦 ...");
            }
        });

        /*
            2. 啓動線程
         */
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        group1.list();
        System.out.println("group1.size ->  " + group1.activeCount());
        System.out.println("----");
        System.out.println("==== 啓動 thread4,驗證 activeCount 含義(只返回活躍數量) ====");

        Thread.sleep(1000);
        System.out.println("==== 創建子group ====");
        ThreadGroup group2 = new ThreadGroup(group1,"Group2");
        Thread t21 = new Thread(group2, () -> {
            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println("t21 被打斷啦 ...");
            }
        });
        t21.start();
        Thread.sleep(1000);
        System.out.println("遍歷下 group1 ,觀察是否加入了...");
        group1.list();
        System.out.println("遍歷下 group2 ...");
        group2.list();
        System.out.println("最後遍歷下最牛逼的 group-main ...");
        Thread.currentThread().getThreadGroup().list();

        Thread.sleep(1000);
        /*
            interrupt
         */
        System.out.println("=== 最後打斷全部線程 ===");
//        group1.destroy();
        group1.interrupt();

        System.out.println("main thread end ...");
    }

輸出:

java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
group1.size ->  3
----
==== 啓動 thread4,驗證 activeCount 含義(只返回活躍數量) ====
==== 創建子group ====
遍歷下 group1 ,觀察是否加入了...
java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
    java.lang.ThreadGroup[name=Group2,maxpri=10]
        Thread[Thread-3,5,Group2]
遍歷下 group2 ...
java.lang.ThreadGroup[name=Group2,maxpri=10]
    Thread[Thread-3,5,Group2]
最後遍歷下最牛逼的 group-main ...
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    Thread[Monitor Ctrl-Break,5,main]
    java.lang.ThreadGroup[name=Group1,maxpri=10]
        Thread[Thread-0,5,Group1]
        Thread[Thread-1,5,Group1]
        Thread[Thread-2,5,Group1]
        java.lang.ThreadGroup[name=Group2,maxpri=10]
            Thread[Thread-3,5,Group2]
=== 最後打斷全部線程 ===
main thread end ...
t1 被打斷啦 ...
t2 被打斷啦 ...
t3 被打斷啦 ...

JVM跑完全程,無任何地方卡住。

=== 點擊查看top目錄 ===

2.3 實戰三 :驗證 enumerate

  • 代碼流程如下:
  1. 測試 enumerate(Thread list[]) ,複製group下面的線程
  2. 測試 enumerate(ThreadGroup list[]) ,複製整個group
  3. 測試發現是淺複製
public static void testCopy() throws Exception {
      ThreadGroup group1 = new ThreadGroup("Group1");
      System.out.println("----");


      Thread t1 = new Thread(group1, () -> {
          Scanner sc = new Scanner(System.in);
          System.out.println("點擊任意鍵喚醒線程 ...");
          sc.nextLine();
      });
      Thread t2 = new Thread(group1, () -> {
          Scanner sc = new Scanner(System.in);
          System.out.println("點擊任意鍵喚醒線程 ...");
          sc.nextLine();

      });
      Thread t3 = new Thread(group1, () -> {
          Scanner sc = new Scanner(System.in);
          System.out.println("點擊任意鍵喚醒線程 ...");
          sc.nextLine();
      });

      group1.list();
      System.out.println("group1.size ->  " + group1.activeCount());

      Thread.sleep(1000);
      System.out.println("----");

      t1.start();
      t2.start();
      t3.start();

      /*
          2. 開始 copy ,保留原 group
          複製threads
       */
      System.out.println("=== 2. begin to copy === ");
      Thread[] threads = new Thread[group1.activeCount()];
      group1.enumerate(threads);
      // 上面僅僅是複製,沒有啓動。
      System.out.println("group1.size ->  " + group1.activeCount());
      System.out.println("----");
      Arrays.stream(threads).forEach(System.out::println);

      Thread.sleep(1000);

      System.out.println(threads[0] == t1);
      System.out.println(threads[1] == t2);
      System.out.println(threads[2] == t3);
      System.out.println("threads 複製 enumerate 僅僅是淺複製 ...");
      System.out.println("group1.size ->  " + group1.activeCount());
      System.out.println("----");

      Thread.sleep(1000);

      /*
          複製 group
       */
      System.out.println("=== 第二波複製 ===");
      System.out.println("Thread.currentThread().getThreadGroup().activeGroupCount() -> " +
              Thread.currentThread().getThreadGroup().activeGroupCount());
      Thread.currentThread().getThreadGroup().list();
      ThreadGroup[] groups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];

      Thread.currentThread().getThreadGroup().enumerate(groups);
      // 上面僅僅是複製,沒有啓動。
      System.out.println("----");

      Thread.sleep(1000);
      System.out.println("=== 複製完了 ===");
      for (ThreadGroup group :
              groups) {
          group.list();
      }

      System.out.println("groups[0] == group1 ? " + (groups[0] == group1));
      System.out.println("group 複製 enumerate 僅僅是淺複製 ...");
      System.out.println("group1.size ->  " + group1.activeCount());
      System.out.println("----");
  }

輸出:

----
java.lang.ThreadGroup[name=Group1,maxpri=10]
group1.size ->  0
----
=== 2. begin to copy === 
group1.size ->  3
----
Thread[Thread-0,5,Group1]
Thread[Thread-1,5,Group1]
Thread[Thread-2,5,Group1]
點擊任意鍵喚醒線程 ...
點擊任意鍵喚醒線程 ...
點擊任意鍵喚醒線程 ...
true
true
true
enumerate 僅僅是淺複製 ...
group1.size ->  3
----
=== 第二波複製 ===
Thread.currentThread().getThreadGroup().activeGroupCount() -> 1
java.lang.ThreadGroup[name=main,maxpri=10]
    Thread[main,5,main]
    Thread[Monitor Ctrl-Break,5,main]
    java.lang.ThreadGroup[name=Group1,maxpri=10]
        Thread[Thread-0,5,Group1]
        Thread[Thread-1,5,Group1]
        Thread[Thread-2,5,Group1]
----
=== 複製完了 ===
java.lang.ThreadGroup[name=Group1,maxpri=10]
    Thread[Thread-0,5,Group1]
    Thread[Thread-1,5,Group1]
    Thread[Thread-2,5,Group1]
groups[0] == group1 ? true
enumerate 僅僅是淺複製 ...
group1.size ->  3
----
...
(輸入任意鍵,線程結束)
  • 結論:淺層複製,只是引用複製。

=== 點擊查看top目錄 ===

2.4 實戰四:測試默認 ThreadGroup

  • 代碼
    public static void test01() throws Exception {
        print();
        new Thread(() -> print(),"A1").start();
        new Thread(() -> new Thread(() -> print(),"B2").start(),"B1").start();
    }
	public static void print(){
       System.out.println("currentThread -> " + Thread.currentThread() + ",group -> " + Thread.currentThread().getThreadGroup());
    }
  • 輸出
currentThread -> Thread[main,5,main],group -> java.lang.ThreadGroup[name=main,maxpri=10]
currentThread -> Thread[A1,5,main],group -> java.lang.ThreadGroup[name=main,maxpri=10]
currentThread -> Thread[B2,5,main],group -> java.lang.ThreadGroup[name=main,maxpri=10]
  • 結論: 不指定 group 的情況下,deamon 、priority 參數隨父 thread。

=== 點擊查看top目錄 ===

2.5 實戰五:測試 Exception 捕獲

  • 代碼
public static void testCatchException() throws Exception{
        ThreadGroup g1 = new ThreadGroup("ThreadGroup");
        Thread t1 = new Thread(g1, () -> { throw new RuntimeException(Thread.currentThread() + "自定義的一個RuntimeException...");});
        t1.start();

        Thread.sleep(1000);

        CatchExceptinoThreadGroup g2 = new CatchExceptinoThreadGroup("CatchExceptionThreadGroup");
        Thread t2 = new Thread(g2, () -> { throw new RuntimeException(Thread.currentThread() + "自定義的一個RuntimeException...");});
        t2.start();
 }
    
 public static class CatchExceptinoThreadGroup extends ThreadGroup{
      public CatchExceptinoThreadGroup(String name) {
          super(name);
      }
      public CatchExceptinoThreadGroup(ThreadGroup parent, String name) {
          super(parent, name);
      }
      @Override
      public void uncaughtException(Thread t, Throwable e) {
          // 這裏可以寫 if else 處理各種各樣的異常
          if(e instanceof RuntimeException){
              System.out.println("### CatchExceptinoThreadGroup catch " + e);
          }
      }
  }
  • 輸出
Exception in thread "Thread-0" java.lang.RuntimeException: Thread[Thread-0,5,ThreadGroup]自定義的一個RuntimeException...
	at indi.sword.util.basic.Thread._06_01_TestThreadGroup.lambda$testCatchException$15(_06_01_TestThreadGroup.java:291)
	at java.lang.Thread.run(Thread.java:748)
### CatchExceptinoThreadGroup catch java.lang.RuntimeException: Thread[Thread-1,5,CatchExceptionThreadGroup]自定義的一個RuntimeException...

  • 解析
  1. g1 線程組(默認組),group內線程拋出異常,無法在外頭捕獲
  2. g2 線程組 (重寫組),group內線程拋出異常,被外層捕獲到了
  • 結論:可以重寫 uncaughtException 方法來進行異常的捕獲。關於 Thread.UncaughtExceptionHandler 下一章節進行講解。

=== 點擊查看top目錄 ===

三、源碼剖析

3.1 Class 類初始化

3.1.1 demo
Thread t1 = new Thread(group1, () -> {
          Scanner sc = new Scanner(System.in);
          System.out.println("點擊任意鍵喚醒線程 ...");
          sc.nextLine();
      });

=== 點擊查看top目錄 ===

3.1.2 Thread 構造方法
  • java.lang.Thread#Thread(java.lang.Runnable, java.lang.String)
public Thread(Runnable target) {
       init(null, target, "Thread-" + nextThreadNum(), 0);
   }

=== 點擊查看top目錄 ===

3.1.3 init 方法
  • java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long)
 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }
  • java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean)
private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc,
                      boolean inheritThreadLocals) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name;
        //當前線程就是該線程的父線程
        Thread parent = currentThread();
        //獲取系統的security
        SecurityManager security = System.getSecurityManager();
        // 如果沒有傳入group
        if (g == null) {
            /* Determine if it's an applet or not */

            /* If there is a security manager, ask the security manager
               what to do. */
            //security不爲null時,線程所在group爲security的group
            if (security != null) {
                g = security.getThreadGroup();
            }

            /* If the security doesn't have a strong opinion of the matter
               use the parent thread group. */
            //security爲null時,直接使用父線程的group
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

        /* checkAccess regardless of whether or not threadgroup is
           explicitly passed in. */
        g.checkAccess();

        /*
         * Do we have the required permissions?
         */
        //授權
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        //將守護線程、優先級等設置爲父線程的對應屬性
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            //創建線程共享變量副本
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;

        /* Set thread ID */
        //分配線程id
        tid = nextThreadID();
    }

分析:

  1. 若 group 沒有指定,那麼使用的是父線程的 group ,daemon 與 priority 參數都跟隨父類。
  2. if (inheritThreadLocals && parent.inheritableThreadLocals != null) 這個具體看上一篇【線程】InheritableThreadLocal 剖析 (十六)

=== 點擊查看top目錄 ===

3.2 activeCount 方法

  • java.lang.ThreadGroup#activeCount
  • 活躍 thread 數
public int activeCount() {
        int result;
        // Snapshot sub-group data so we don't hold this lock
        // while our children are computing.
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            result = nthreads; // 獲取線程數
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            result += groupsSnapshot[i].activeCount();
        }
        return result;
    }

  • 解析:
  1. synchronized 方法塊,確保線程安全
  2. 遞歸統計。(result = nthreads; // 獲取線程數)

=== 點擊查看top目錄 ===

3.3 activeGroupCount 方法

  • java.lang.ThreadGroup#activeGroupCount
 public int activeGroupCount() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        int n = ngroupsSnapshot; // 獲取 group 數
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            n += groupsSnapshot[i].activeGroupCount(); // 遞歸 
        }
        return n;
    }
  • 解析:
  1. synchronized 方法塊,確保線程安全
  2. 遞歸統計。(int n = ngroupsSnapshot; // 獲取 group 數 )

=== 點擊查看top目錄 ===

3.4 list 方法

  • 用於打印操作
  • java.lang.ThreadGroup#list()
public void list() {
        list(System.out, 0);
    }
  • java.lang.ThreadGroup#list(java.io.PrintStream, int) 方法
void list(PrintStream out, int indent) {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            for (int j = 0 ; j < indent ; j++) {
                out.print(" ");
            }
            out.println(this);
            indent += 4;
            for (int i = 0 ; i < nthreads ; i++) {
                for (int j = 0 ; j < indent ; j++) {
                    out.print(" ");
                }
                out.println(threads[i]);
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].list(out, indent); //遞歸
        }
    }
  • 解析:
  1. synchronized 方法塊,確保線程安全
  2. 參數 PrintStream 指定輸出流,默認控制檯 (System.out,)
  3. 參數 indent ,打印前置多少個空格,目的是爲了層級顯示。
  4. groupsSnapshot[i].list(out, indent); 遞歸進行 list 打印操作。

=== 點擊查看top目錄 ===

3.5 enumerate 方法

  • java.lang.ThreadGroup#enumerate(java.lang.Thread[])
  • 用於複製操作
    public int enumerate(Thread list[]) {
        checkAccess();
        return enumerate(list, 0, true);
    }

  • java.lang.ThreadGroup#enumerate(java.lang.Thread[], int, boolean) 方法
private int enumerate(Thread list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int nt = nthreads;
            if (nt > list.length - n) {
                nt = list.length - n;
            }
            for (int i = 0; i < nt; i++) {
                if (threads[i].isAlive()) {
                    list[n++] = threads[i];
                }
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true); //遞歸
            }
        }
        return n;
    }
  • 解析:
  1. synchronized 方法塊,確保線程安全
  2. Arrays.copyOf 進行復制(最終調用Native方法 java.lang.System#arraycopy )
  3. groupsSnapshot[i].enumerate(list, n, true); 遞歸進行復制

=== 點擊查看top目錄 ===

3.6 interrupt 方法

  • java.lang.ThreadGroup#interrupt
public final void interrupt() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            for (int i = 0 ; i < nthreads ; i++) {
                threads[i].interrupt();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].interrupt(); //遞歸
        }
    }
  • 解析:
  1. synchronized 方法塊,確保線程安全
  2. groupsSnapshot[i].interrupt(); 內部遞歸調用 interrupt

=== 點擊查看top目錄 ===

3.7 uncaughtException 方法

    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }
  • 解析:
  1. 對於子Thread拋出的Exception,遞歸交由父 ThreadGroup 定義的 uncaughtException 方法來處理。

四、番外篇

下一章節:【線程】Thread.UncaughtExceptionHandler 實戰與剖析 (十八)
上一章節:【線程】InheritableThreadLocal 剖析 (十六)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章