在前文中我們已經學習了:線程的基本情況、如何創建多線程、線程的生命周期。利用已有知識我們已經可以寫出如何利用多線程處理大量任務這樣簡單的程序。但是當應用場景復雜時,我們還需要從管理控制入手,更好的操縱多線程。在第一節中我們講過,使用多線程的好處之一就是我們可以通過編碼和已有類庫更好的管理和控制多線程。接下來我會詳細的介紹如何管理多線程,包括:對線程的等待、守護線程、線程的睡眠、線程的突然停止、線程的讓步、線程的優先級等。由於內容比較多,本節先介紹前兩部分:對線程的等待、守護線程
1、線程的等待
我們常常對同一件事情進行切割,分成多干件小的事情后,再開辟多條線程來處理(有點分治的味道)。在多件事情處理完成之后我們需要再統一的處理。這樣說有點枯燥:比如我們要導出一份文件,這個文件的行數非常多大概幾十萬行數據。直接導出也可以,會導致系統卡一下,嚴重一點的可能會超時。這時候怎么辦呢?我們可以分成若干份,比如每五千條數據是一份數據,然后用一條線程去導出到文件中,這樣就會生成若干份文件。當所有的文件都導出后,我們把這些數據匯總一下就OK了。但是這里有一個問題,什么時候所有線程都導完數據了呢?我們總不能一個一個的去檢查文件是否存在吧。
這里java為我們提供了一個專門用於等待的方法Join().當某條線程執行了其他線程的join()方法以后,當前線程就會阻塞,直到其他線程執行結束以后,才可以運行。
如下述代碼:
1 public class newThread extends Thread 2 { 3 public newThread(String name) 4 { 5 super(name); 6 } 7 public void run() 8 { 9 int i=100; 10 while(i-->0) 11 { 12 sleep(100); 13 } 14 } 15 16 public static void main(String args)throws Exception 17 { 18 Thread son_1=new newThread("thread-1"); 19 Thread son_2=new newThread("thread-2"); 20 son_1.start(); 21 son_2.start(); 22 son_1.join();//注意這里 23 son_2.join(); 24 System.out.println("all threads is over") 25 } 26 }
我們分別啟動thread 1 和thread 2。這兩條線程會分別運行,互相不影響,然后我們用主線程來等待線程1,直到它結束,我們才開始等待線程2。如果線程1比線程2的用時長的話,再我們等待完線程1 后,線程2將無需等待,直接輸出結果。
join方法共有三種形式的重載
1 join() 2 join(long millis)//
3 join(long millis,int nanos)//millis 毫秒 nanos微秒
第一個方法時直到等待線程結束才繼續向下運行,第二和第三個方法則是在限定時間內等待,如果時間結束將不會繼續等待(可以用於超時判斷),注意第三個方法並不常用,這里主要是因為java和硬件對時間的把控根本難以精確到微秒級,所以並不能很准確到控制。
2、守護線程
守護線程(Daemon Thread)又叫做精靈線程、后台線程。乍一聽守護二字,覺得好像很厲害的意思,像是在保護其他線程的線程,其實很簡單,並沒有那么玄。所有的語言設計,都是為了更方便的使用。(防盜連接:本文首發自王若伊_恩賜解脫http://www.cnblogs.com/jilodream/ )
設想這樣一個場景:在一個考場內,考生們都在埋頭答卷,除了考生,還有監考老師,他的職責就是為了維護考場的秩序,對於有困難的考生提供幫助。當所有的考生都交卷后(或者時間到了以后,考生被強制交卷(結束線程)),這個監考老師的義務已經完成,無需繼續運行。簡而言之,這個監考老師是為了其他學生提供后台服務的,當所有的考生都已經停筆后,老師的義務也已經結束了,可以退場了。
有些人看到這里,可能會覺得,這還不簡單,我們只要如線程等待讓老師等待所有的學生都結束之后,不就可以了么?!這個回答對也不對。對的是老師的線程的確是在等待學生線程結束,不對的地方是老師線程在等待的同時,還在提供服務,並不是所有學生停筆后收卷這么簡單。在Jvm中有一個所有開發者都熟悉的線程GC,它就是在其他線程運行的同時,默默的提供服務,當其他線程結束后,他又默默的退場。
如下代碼
1 public class newThread extends Thread 2 { 3 public newThread(String name) 4 { 5 super(name); 6 } 7 public void run() 8 { 9 int i=100; 10 while(i-->0) 11 { 12 sleep(100); 13 } 14 } 15 16 public static void main(String args)throws Exception 17 { 18 Thread son_1=new newThread("thread-1"); 19 Thread son_2=new newThread("thread-2"); 20 Thread daemonThread=new newThread("thread-daemonThread"); 21 son_1.start(); 22 son_2.start(); 23 24 daemonThread.setDaemon(true);//注意看這里 25 daemonThread.start(); 26 son_1.join(); 27 son_2.join(); 28 System.out.println("all son_threads is over") 29 } 30 } 31 32 class DaemonThread 33 { 34 public DaemonThread(String name) 35 { 36 super(name); 37 } 38 public void run() 39 { 40 int i=1000; 41 while(i-->0) 42 { 43 sleep(100); 44 } 45 } 46 }
在代碼中 Daemonthread線程被設置為后台線程,則當主線程等待子線程結束后, Daemonthread線程也會結束,不會繼續運行。我們可以用這種方式設置一種線程,專門用來維護其他線程的正常運行,比如后台計數,計時,查詢各個客戶端是否保持在線(心跳),推送事件等。
java還專門提供了 IsDaemon()的方法來判斷這個線程是否為后台線程。
這里有一點注意的是,如果要設置一個線程為后台線程,必須要在這個線程還沒有開始“就緒”(start())時設置,否則會引發非法的線程狀態異常