Java多線程核心技術(六)線程組與線程異常

本文應注重掌握以下知識點:html

  1. 線程組的使用
  2. 如何切換線程狀態
  3. SimpleDataFormat 類與多線程的解決辦法
  4. 如何處理線程的異常

1.線程的狀態

線程對象在不一樣運行時期有不一樣的狀態,狀態信息就處於State枚舉類中,如圖所示:編程

線程狀態

  1. 初始(NEW):新建立了一個線程對象,但尚未調用start()方法。數組

  2. 運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱爲「運行」。安全

    線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處於就緒狀態(ready)。就緒狀態的線程在得到CPU時間片後變爲運行中狀態(running)。網絡

  3. 阻塞(BLOCKED):表示線程阻塞於鎖。多線程

  4. 等待(WAITING):進入該狀態的線程須要等待其餘線程作出一些特定動做(通知或中斷)。併發

  5. 超時等待(TIMED_WAITING):該狀態不一樣於WAITING,它能夠在指定的時間後自行返回。ide

  6. 終止(TERMINATED):表示該線程已經執行完畢。學習

調用與線程有關的方法是形成線程狀態改變的主要緣由,其關係如圖所示:(圖片來源於網絡)this

2.線程組

能夠把線程歸屬到某一個線程組中,線程組中能夠有線程對象,也能夠有線程組,組中還能夠有線程。

下面看下線程組的使用示例:

public class Group implements Runnable {

    public static void main(String[] args) {
        Group runnable = new Group();
        ThreadGroup threadGroup = new ThreadGroup("個人線程組");
        Thread threadA = new Thread(threadGroup,runnable);
        Thread threadB = new Thread(threadGroup,runnable);
        threadA.start();
        threadB.start();
        System.out.println("活動的線程"+threadGroup.activeCount());
        System.out.println("線程組的名稱"+threadGroup.getName());
    }

    @Override
    public void run() {
        while (true){
            System.out.println("Thread-Name: "+Thread.currentThread().getName());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

運行結果:

活動的線程2
線程組的名稱個人線程組
Thread-Name: Thread-0
Thread-Name: Thread-1

控制檯中的信息代表有2個線程在名爲「個人線程組」下活動。

上面的線程樹結構圖中顯示,全部的線程與線程組都在系統線程組下,下面演示如何建立具備多級關聯關係的線程結構,並獲取相關線程信息。

示例代碼:

public class Group implements Runnable {

    public static void main(String[] args) {
        ThreadGroup threadGroupMain = Thread.currentThread().getThreadGroup();
        Group runnable = new Group();
        ThreadGroup threadGroup = new ThreadGroup(threadGroupMain,"個人線程組");
        Thread threadA = new Thread(threadGroup,runnable);
        Thread threadB = new Thread(threadGroup,runnable);
        threadA.start();
        threadB.start();
        System.out.println("系統線程組的名字:"+Thread.currentThread().getThreadGroup().getName());
        System.out.println("系統線程組中有多少子線程:"+Thread.currentThread().getThreadGroup().activeCount());
        Thread[] threads1 = new Thread[threadGroupMain.activeCount()];
        threadGroupMain.enumerate(threads1);
        System.out.println("這些子線程具體是:");
        for (int i = 0; i < threads1.length; i++) {
            System.out.println(threads1[i].getName());
        }
        System.out.println("系統線程組中有多少子線程組:"+Thread.currentThread().getThreadGroup().activeGroupCount());
        ThreadGroup[] listGroup = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
        //enumerate方法將子線程組以複製的形式拷貝到數組中,並返回拷貝的數量
        Thread.currentThread().getThreadGroup().enumerate(listGroup);
        System.out.println("子線程組的名字是:"+listGroup[0].getName());
        Thread[] threads = new Thread[listGroup[0].activeCount()];
        listGroup[0].enumerate(threads);
        System.out.println("子線程組中的線程有:");
        for (int i = 0; i < threads.length; i++) {
            System.out.println(threads[i].getName());
        }
    }

    @Override
    public void run() {
        while (true){
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

運行結果:

系統線程組的名字:main
系統線程組中有多少子線程:4
這些子線程具體是:
main
Monitor Ctrl-Break
Thread-0
Thread-1
系統線程組中有多少子線程組:1
子線程組的名字是:個人線程組
子線程組中的線程有:
Thread-0
Thread-1

須要說明的是,在實例化一個ThreadGroup線程組x時若是不指定所屬的線程組,則x線程組自動歸屬到當前線程對象所屬的線程組中,也就是隱式的當前線程組中添加了一個子線程組。

線程組批量操做

經過將線程歸屬到線程組中,當調用線程組ThreadGroup的interrupt()方法時,能夠將該組中的全部正在運行的線程批量中止。

3.SimpleDataFormat非線程安全

SimpleDataFormat主要負責日期的轉換與格式化,但在多線程的環境中,使用此類容易形成數據及處理的不許確,由於SimpleDataFormat並非安全的。

解決方法有兩種,一是每一個線程都new一個新的SimpleDataFormat實例對象;二是利用ThreadLocal類將SimpleDataFormat對象綁定到線程上。

4.線程中出現異常的處理

在 Java 的多線程技術中,能夠對多線程中的異常進行「捕捉」,使用的是 UncaughtExceptionHandler類,從而能夠對發生的異常進行有效的處理。

thread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
     @Override
     public void uncaughtException(Thread t, Throwable e) {
          System.out.println("線程"+t.getName()+"出現了異常");
          e.printStackTrace();
     }
});

方法setUncaughtExceptionHandler()是給指定的線程對象設置的異常處理器。在Thread類中還可使用setDefaultUncaughtExceptionHandler()方法對全部線程對象設置異常處理器。示例代碼以下:

MyThread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
       @Override
       public void uncaughtException(Thread t, Throwable e) {
           System.out.println("線程"+t.getName()+"出現了異常");
           e.printStackTrace();
       }
});

方法setDefaultUncaughtExceptionHandler()的做用是爲指定的線程類的全部線程對象設置默認的異常處理器。

5.線程組中出現異常的處理

使用重寫uncaughtException方法處理組內線程的異常時,每一個線程內部不要有異常catch語句,若是有catch語句,則public void uncaughtException(Thread t, Throwable e) 方法不執行。

ThreadGroup group = new ThreadGroup(""){
      @Override
      public void uncaughtException(Thread t, Throwable e) {
             super.uncaughtException(t, e);
             //一個線程出現異常,中斷組內全部線程
             this.interrupt();
      }
};

前面介紹了線程對象的異常處理,線程類的異常處理,線程組的異常處理。將它們放一塊兒會出現什麼效果呢?

示例代碼:

public class MyThread{

    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("ThreadGroup"){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("線程組的異常處理");
                super.uncaughtException(t, e);
            }
        };
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("線程類的異常處理");
            }
        });
        Thread thread = new Thread(threadGroup,"Thread"){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"執行");
                int i= 2/0;
            }
        };
        thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("線程對象的異常處理");
            }
        });
        thread.start();
    }

}

運行結果:

Thread執行
線程對象的異常處理

註釋掉線程對象的異常處理以後,再次運行:

public class MyThread{

    public static void main(String[] args) {
        ThreadGroup threadGroup = new ThreadGroup("ThreadGroup"){
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("線程組的異常處理");
                super.uncaughtException(t, e);
            }
        };
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println("線程類的異常處理");
            }
        });
        Thread thread = new Thread(threadGroup,"Thread"){
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"執行");
                int i= 2/0;
            }
        };
//        thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
//            @Override
//            public void uncaughtException(Thread t, Throwable e) {
//                System.out.println("線程對象的異常處理");
//            }
//        });
        thread.start();
    }

}

運行結果:

Thread執行
線程組的異常處理
線程類的異常處理

6.文末總結

本文彌補了前面幾個文章的技術空白點。到此,Java多線程編程核心技術的學習告一段落。

參考

《Java多線程編程核心技術》高洪巖著

擴展

Java多線程編程核心技術(一)Java多線程技能

Java多線程編程核心技術(二)對象及變量的併發訪問

Java多線程編程核心技術(三)多線程通訊

Java多線程核心技術(四)Lock的使用

Java多線程核心技術(五)單例模式與多線程

相關文章
相關標籤/搜索