本文應注重掌握以下知識點:html
線程對象在不一樣運行時期有不一樣的狀態,狀態信息就處於State枚舉類中,如圖所示:編程
初始(NEW):新建立了一個線程對象,但尚未調用start()方法。數組
運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱爲「運行」。安全
線程對象建立後,其餘線程(好比main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處於就緒狀態(ready)。就緒狀態的線程在得到CPU時間片後變爲運行中狀態(running)。網絡
阻塞(BLOCKED):表示線程阻塞於鎖。多線程
等待(WAITING):進入該狀態的線程須要等待其餘線程作出一些特定動做(通知或中斷)。併發
超時等待(TIMED_WAITING):該狀態不一樣於WAITING,它能夠在指定的時間後自行返回。ide
終止(TERMINATED):表示該線程已經執行完畢。學習
調用與線程有關的方法是形成線程狀態改變的主要緣由,其關係如圖所示:(圖片來源於網絡)this
能夠把線程歸屬到某一個線程組中,線程組中能夠有線程對象,也能夠有線程組,組中還能夠有線程。
下面看下線程組的使用示例:
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()方法時,能夠將該組中的全部正在運行的線程批量中止。
SimpleDataFormat主要負責日期的轉換與格式化,但在多線程的環境中,使用此類容易形成數據及處理的不許確,由於SimpleDataFormat並非安全的。
解決方法有兩種,一是每一個線程都new一個新的SimpleDataFormat實例對象;二是利用ThreadLocal類將SimpleDataFormat對象綁定到線程上。
在 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()的做用是爲指定的線程類的全部線程對象設置默認的異常處理器。
使用重寫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執行 線程組的異常處理 線程類的異常處理
本文彌補了前面幾個文章的技術空白點。到此,Java多線程編程核心技術的學習告一段落。
《Java多線程編程核心技術》高洪巖著