Markdown版本筆記 | 個人GitHub首頁 | 個人博客 | 個人微信 | 個人郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
線程 Thread Runnable 守護線程【API】html
目錄
Runnable
Thread
簡介
構造方法
常量
靜態方法
公共方法
不經常使用方法
已過期方法
對 join 方法的測試案例
守護線程
守護線程使用注意
守護線程的意義
守護線程的使用案例
ThreadGroup 線程組
Runnable
JDK文檔java
全部已知實現類:
Thread
, TimerTask
, AsyncBoxView.ChildState, FutureTask, RenderableImageProducer, SwingWorkergit
Runnable 接口應該由那些打算經過某一線程
執行其實例的類來實現。
設計該接口的目的是爲但願在活動時執行代碼的對象提供一個公共協議。例如,Thread 類實現了 Runnable。激活的意思是說某個線程已啓動而且還沒有中止。程序員
此外,Runnable 爲非 Thread 子類的類提供了一種激活方式。經過實例化某個 Thread 實例並將自身做爲運行目標,就能夠運行實現 Runnable 的類而無需建立 Thread 的子類。大多數狀況下,若是隻想重寫 run() 方法,而不重寫其餘 Thread 方法,那麼應使用 Runnable 接口。這很重要,由於除非程序員打算修改或加強類的基本行爲,不然不該爲該類建立子類。github
使用實現接口 Runnable 的對象建立一個線程時,啓動該線程將致使在獨立執行的線程中調用對象的 run 方法。數據庫
方法 run 的常規協定是,它可能執行任何所需的動做。api
Thread
JDK文檔數組
簡介
public class Thread extends Object implements Runnable
Thread 是程序中的執行線程。Java 虛擬機容許應用程序併發地運行多個執行線程。安全
每一個線程都有一個優先級
,高優先級線程的執行優先於低優先級線程。每一個線程均可以或不能夠標記爲一個守護程序
。當某個線程中運行的代碼建立一個新 Thread 對象時,該新線程的初始優先級被設定爲建立線程的優先級,而且當且僅當建立線程是守護線程時,新線程纔是守護程序。服務器
當 Java 虛擬機啓動時,一般都會有單個非守護線程(它一般會調用某個指定類的 main 方法)。Java 虛擬機會繼續執行線程,直到下列任一狀況出現時爲止:
- 調用了 Runtime 類的 exit 方法,而且安全管理器容許退出操做發生。
- 非守護線程的全部線程都已中止運行,不管是經過從對 run 方法的調用中返回,仍是經過拋出一個傳播到 run 方法以外的異常。
建立新執行線程有兩種方法。一種方法是將類聲明爲 Thread 的子類。該子類應重寫 Thread 類的 run 方法。接下來能夠分配並啓動該子類的實例。
建立線程的另外一種方法是聲明實現 Runnable 接口的類。該類而後實現 run 方法。而後能夠分配該類的實例,在建立 Thread 時做爲一個參數來傳遞並啓動。
每一個線程都有一個標識名,多個線程能夠同名
。若是線程建立時沒有指定標識名,就會爲其生成一個新名稱。
構造方法
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
分配新的 Thread 對象,將target做爲其運行對象,將name做爲其名稱,做爲group所引用的線程組的一員,並具備指定的堆棧大小
- group - 線程組。線程組能夠批量的管理線程或線程組對象,有效的對線程或線程組進行組織。
- target - 其 run 方法被調用的對象。
- 若是 target 參數不是 null,則 target 的 run 方法在啓動該線程時調用。若是 target 參數爲 null,則該線程的 run 方法在該線程啓動時調用。
- 新建立線程的優先級被設定爲建立該線程的線程的優先級,即當前正在運行的線程的優先級。方法 setPriority 可用於將優先級更改成一個新值。
- 當且僅當建立新線程的線程當前被標記爲守護線程時,新建立的線程才被標記爲守護線程。方法 setDaemon 可用於改變線程是否爲守護線程。
- name - 新線程的名稱。自動生成的名稱的形式爲
Thread- n
,其中的 n 爲整數。 - stackSize - 新線程的
預期堆棧大小
,爲零時表示忽略該參數。
堆棧大小是虛擬機要爲該線程堆棧分配的地址空間的近似字節數。
stackSize 參數的做用具備高度的平臺依賴性,在某些平臺上,stackSize 參數的值不管如何不會起任何做用。
做爲建議,可讓虛擬機自由處理 stackSize 參數。
其餘構造方法
- Thread()
- Thread(String name)
- Thread(Runnable target)
- Thread(Runnable target, String name)
- Thread(ThreadGroup group, String name)
- Thread(ThreadGroup group, Runnable target)
- Thread(ThreadGroup group, Runnable target, String name)
常量
public final static int MAX_PRIORITY
= 10; //The maximum priority that a thread can have.
public final static int NORM_PRIORITY
= 5; //The default priority that is assigned to a thread.
public final static int MIN_PRIORITY
= 1; //The minimum priority that a thread can have.
靜態方法
- static int activeCount() 返回當前線程的
線程組中活動線程的數目
。 - static Thread currentThread() 返回對當前
正在執行的線程對象
的引用。 - static void dumpStack() 將當前線程的
堆棧跟蹤
打印至標準錯誤流。效果相似於new Throwable().printStackTrace()
。該方法僅用於調試。
// 調用 Thread.dumpStack() 後打印的內容 java.lang.Exception: Stack trace at java.lang.Thread.dumpStack(Thread.java:1329) at Test.main(Test.java:6) new Throwable().printStackTrace(); java.lang.Throwable at Test.main(Test.java:6)
- static int enumerate(Thread[] tarray) 將當前線程的線程組及其子組中的活動線程複製到數組中。該方法只調用當前線程的線程組的 enumerate 方法,且帶有數組參數。返回放入該數組的線程數。
- static Map getAllStackTraces() 返回全部活動線程的堆棧跟蹤的一個映射。
- 映射的鍵是
線程
,而每一個映射值都是一個StackTraceElement 數組
,該數組表示相應 Thread 的堆棧轉儲。 返回的堆棧跟蹤的格式都是針對 getStackTrace 方法指定的。 - 在調用該方法的同時,線程可能也在執行。每一個線程的堆棧跟蹤僅表明一個
快照
,而且每一個堆棧跟蹤均可以在不一樣時間得到。若是虛擬機沒有線程的堆棧跟蹤信息,則映射值中將返回一個零長度數組。
- 映射的鍵是
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces(); for (Thread thread : map.keySet()) { System.out.println(thread.toString() + " " + Arrays.toString(map.get(thread))); } Thread[Finalizer,8,system] [java.lang.Object.wait(Native Method), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143), java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164), java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)] Thread[Attach Listener,5,system] [] Thread[Signal Dispatcher,9,system] [] Thread[Reference Handler,10,system] [java.lang.Object.wait(Native Method), java.lang.Object.wait(Object.java:502), java.lang.ref.Reference.tryHandlePending(Reference.java:191), java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)] Thread[main,5,main] [java.lang.Thread.dumpThreads(Native Method), java.lang.Thread.getAllStackTraces(Thread.java:1603), Test.main(Test.java:8)]
- static Thread.UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() 返回線程因爲未捕獲到異常而忽然終止時調用的默認處理程序。若是返回值爲 null,則沒有默認處理程序。
- static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 設置當線程因爲未捕獲到異常而忽然終止,而且沒有爲該線程定義其餘處理程序時所調用的默認處理程序
- static boolean holdsLock(Object obj) 當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。該方法旨在使程序可以斷言當前線程已經保持一個指定的鎖:assert Thread.holdsLock(obj);
- static boolean interrupted() 測試當前線程是否已經中斷。
- static void sleep(long millis) 在指定的毫秒數內讓【當前】正在執行的線程休眠。
- static void sleep(long millis, int nanos) 在指定的毫秒數+納秒數內讓當前正在執行的線程休眠。nanos 的值必須在 0-999999 之間。
- 此操做受到系統計時器和調度程序精度和準確性的影響。該線程不丟失任何監視器的所屬權。
- 若是任何線程中斷了當前線程,則拋出 InterruptedException,當拋出該異常時,當前線程的 中斷狀態 被清除。
- static void yield() 暫停當前正在執行的線程對象,並執行其餘線程。
公共方法
- long getId() 返回該線程的標識符。線程 ID 是一個正的 long 數,在建立該線程時生成。線程 ID 是惟一的,並終生不變。線程終止時,該線程 ID 能夠被從新使用。
- String getName() 返回該線程的名稱。
- void setName(String name) 改變線程名稱,使之與參數 name 相同。
- int getPriority() 返回線程的優先級。
- void setPriority(int newPriority) 更改線程的優先級。
- StackTraceElement[] getStackTrace() 返回一個表示該線程堆棧轉儲的堆棧跟蹤元素數組。
- 若是該線程還沒有啓動或已經終止,則該方法將返回一個零長度數組。
- 若是返回的數組不是零長度的,則其第一個元素表明堆棧頂,它是該序列中最新的方法調用。最後一個元素表明堆棧底,是該序列中最舊的方法調用。
Thread.State getState() 返回該線程的狀態。該方法用於監視系統狀態,不用於同步控制。
- Thread.State.NEW 至今還沒有啓動的線程處於這種狀態。
- Thread.State.RUNNABLE 正在 Java 虛擬機中執行的線程處於這種狀態。
- Thread.State.BLOCKED 受阻塞並等待某個監視器鎖的線程處於這種狀態。
- Thread.State.WAITING 無限期地等待另外一個線程來執行某一特定操做的線程處於這種狀態。
- Thread.State.TIMED_WAITING 等待另外一個線程來執行取決於指定等待時間的操做的線程處於這種狀態。
- Thread.State.TERMINATED 已退出的線程處於這種狀態。
- 在給定時間點上,一個線程只能處於一種狀態。這些狀態是虛擬機狀態,它們並無反映全部操做系統線程狀態。
Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() 返回該線程因爲未捕獲到異常而忽然終止時調用的處理程序。
- void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) 設置該線程因爲未捕獲到異常而忽然終止時調用的處理程序。
- void interrupt() 中斷線程。若是線程在調用 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程當中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。
- boolean isAlive() 測試線程是否處於活動狀態。若是線程已經啓動且還沒有終止,則爲活動狀態。
- boolean isInterrupted() 測試線程是否已經中斷。線程的 中斷狀態 不受該方法的影響。
- void run() 若是該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;不然,該方法不執行任何操做並返回。
- boolean isDaemon() 測試該線程是否爲守護線程。
- void setDaemon(boolean on) 將該線程標記爲守護線程或用戶線程。當正在運行的線程都是守護線程是,Java虛擬機退出。該方法必須在啓動線程前使用。
- void start() 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
- 結果是兩個線程併發地運行;當前線程(從調用返回給 start 方法)和另外一個線程(執行其 run 方法)。
- 屢次啓動一個線程是非法的(會拋出IllegalThreadStateException)。特別是當線程已經結束執行後,不能再從新啓動。
- String toString() 返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。
Thread[Thread-0,5,main]
,Thread[main,5,main]
不經常使用方法
- void checkAccess() 斷定當前運行的線程是否有權修改該線程。若是有安全管理器,則調用其 checkAccess 方法,並將該線程做爲其參數。若是不容許當前線程訪問該線程,拋出 SecurityException。
- ClassLoader getContextClassLoader() 返回該線程的上下文 ClassLoader。
- 上下文 ClassLoader 由線程建立者提供,供運行於該線程中的代碼在加載類和資源時使用。
- 若是未設定,則默認爲父線程的 ClassLoader 上下文。原始線程的上下文 ClassLoader 一般設定爲用於加載應用程序的類加載器。
- void setContextClassLoader(ClassLoader cl) 設置該線程的上下文 ClassLoader。上下文 ClassLoader 能夠在建立線程設置,並容許建立者在加載類和資源時向該線程中運行的代碼提供適當的類加載器。
- ThreadGroup getThreadGroup() 返回該線程所屬的線程組。若是該線程已經終止(中止運行),該方法則返回 null。
System.out.println(new Thread().getThreadGroup()); java.lang.ThreadGroup[name=main,maxpri=10]
void join() 讓父線程等待子線程結束以後才能繼續運行。
- When we call this method using a thread object, it suspends the execution of the calling thread until the object called finishes its execution.當咱們調用某個線程的這個方法時,這個方法會掛起調用線程,直到被調用線程結束執行,調用線程纔會繼續執行。
- 若是任何線程中斷了當前線程則拋出 InterruptedException。當拋出該異常時,當前線程的 中斷狀態 被清除。
void join(long millis) 等待該線程終止的時間最長爲 millis 毫秒。
- void join(long millis, int nanos) 等待該線程終止的時間最長爲 millis 毫秒+nanos 納秒。nanos 的值必須在 0-999999 之間。
已過期方法
- int countStackFrames() 已過期
- void destroy() 已過期
- void resume() 已過期
- void stop() 已過期
- void stop(Throwable obj) 已過期
- void suspend() 已過期
對 join 方法的測試案例
Thread 類中的 join 方法的主要做用就是同步,它可使得線程之間的並行執行變爲串行執行
。
例以下面代碼的意思就是:
Thread t1 = new TestThread("子線程1"); Thread t2 = new TestThread("子線程2"); t1.start();//子線程1開始執行 t1.join();//main 線程會放棄 cpu 控制權,直到線程 t1 執行完畢才獲取 cpu 控制權 t2.start();//因此直到線程 t1 執行完畢纔會執行子線程2
程序在 main 線程(父線程)中調用 t1 線程的 join 方法,則main 線程放棄 cpu 控制權
,並返回 t1 線程繼續執行直到線程 t1 執行完畢。
因此結果是 t1 線程執行完後,纔回到主線程執行,至關於在 main 線程中同步執行 t1 線程
,t1 執行完了,main 線程纔有執行的機會。
public static void main(String[] args) { Thread thread1 = new Thread(() -> { System.out.println("線程1開始執行"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程1執行完畢"); }); Thread thread2 = new Thread(() -> { System.out.println("線程2開始執行"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("線程2執行完畢"); }); thread1.start(); try { thread1.join(); } catch (InterruptedException e) { e.printStackTrace(); } thread2.start(); }
結果爲:
線程1開始執行 線程1執行完畢 線程2開始執行 線程2執行完畢
若果沒有 thread1.join() 結果爲:
線程1開始執行 線程2開始執行 線程2執行完畢 線程1執行完畢
守護線程
在Java中有兩類線程:User Thread(用戶線程)、Daemon Thread(守護線程) 。
守護線程也稱「服務線程」,守護線程會在沒有用戶線程可服務時自動離開
。
守護線程的優先級比較低,用於爲系統中的其它對象和線程提供服務。
Daemon的做用是爲其餘線程的運行提供便利服務,守護線程最典型的應用就是 GC (垃圾回收器),它就是一個很稱職的守護者。
User和Daemon二者幾乎沒有區別,惟一的不一樣之處就在於虛擬機的離開:若是 User Thread已經所有退出運行了,只剩下Daemon Thread存在了,虛擬機也就退出了
。 由於沒有了被守護者,Daemon也就沒有工做可作了,也就沒有繼續運行程序的必要了。
守護線程並不是只有虛擬機內部提供,用戶在編寫程序時也能夠本身設置守護線程。
Thread daemonTread = new Thread(); daemonThread.setDaemon(true); // 設定 daemonThread 爲 守護線程 daemonThread.isDaemon(); // 驗證當前線程是否爲守護線程,返回 true 則爲守護線程
守護線程使用注意
setDaemon(true)
必須在 start() 以前設置,不然會拋出一個 IllegalThreadStateException 異常,你不能把正在運行的常規線程設置爲守護線程。
在Daemon線程中產生的新線程也是Daemon的。
Thread thread = new Thread(() -> System.out.println(new Thread().isDaemon()));//true thread.setDaemon(true); thread.start();
守護線程應該永遠不去訪問固有資源,如文件、數據庫,由於它會在任什麼時候候甚至在一個操做的中間發生中斷。
public class Test { public static void main(String[] args) { Thread thread = new Thread(() -> { try { System.out.println("start"); Thread.sleep(1000);//阻塞1秒後運行 FileWriter writer = new FileWriter(new File("d://daemon.txt"), true); writer.write("daemon"); writer.close(); System.out.println("end"); } catch (Exception e) { e.printStackTrace(); } }); thread.setDaemon(true); //設置守護線程時不會有任何內容寫入文件 thread.start(); //開始執行分進程 } }
結果,字符串並無寫入指定文件。緣由很簡單,直到主線程完成,守護線程仍處於1秒的阻塞狀態,主線程很快就運行完了,而後虛擬機就退出了,Daemon中止服務,輸出操做天然失敗了。
守護線程的意義
用個比較通俗的比喻,任何一個守護線程都是整個JVM中全部非守護線程的保姆:只要當前JVM實例中尚存在任何一個非守護線程沒有結束,守護線程就所有工做;只有當最後一個非守護線程結束時,守護線程隨着JVM一同結束工做。
好比你正在用 Java 寫成的編輯器寫 Word 文檔,你處理敲鍵盤事件的線程是個非守護線程, 除此以外,後臺還有一個進行拼寫檢查的線程,它是個守護線程
,他儘可能不打擾你寫稿子,他們能夠同時進行,當守護線程發現有拼寫錯誤時在狀態條顯示錯誤,可是你能夠忽略。
好比城堡門前有個衛兵(守護線程),裏面有諸侯(非守護線程),他們是能夠同時幹着各自的活兒,可是若是城堡裏面的人都搬走了, 那麼衛兵也就沒有存在的意義了。
好比在使用長鏈接的comet服務端推送技術中,將消息推送線程設置爲守護線程,服務於ChatServlet的servlet用戶線程,在servlet的init啓動消息線程,servlet一旦初始化後,一直存在服務器,servlet摧毀後,消息線程自動退出。
守護線程的使用案例
public class Test { static String account; public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("用戶輸入的帳號爲:" + account); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }); thread.setDaemon(true); //設定爲守護線程,當main線程結束後,此線程也結束了 thread.start(); Scanner scanner = new Scanner(System.in); while (true) { System.out.println("請輸入你的帳號:"); account = scanner.nextLine(); System.out.println(account + " 歡迎光臨"); scanner.close(); break; } } }
ThreadGroup 線程組
public class ThreadGroup extends Object implements Thread.UncaughtExceptionHandler
線程組表示一個線程的集合,能夠用來管理一組線程,實際沒有什麼大的用處。
線程組也能夠包含其餘線程組,線程組構成一棵樹,在樹中,除了初始線程組外,每一個線程組都有一個父線程組。
容許線程訪問有關本身的線程組的信息,可是不容許它訪問有關其線程組的父線程組或其餘任何線程組的信息。
main方法執行後,將自動建立system線程組
和main線程組
,main方法所在線程存放在main線程組中。
public class Test { public static void main(String[] args) { ThreadGroup group = Thread.currentThread().getThreadGroup(); System.out.println(group);//java.lang.ThreadGroup[name=main,maxpri=10] Thread[] threads = new Thread[group.activeCount()]; group.enumerate(threads); for (Thread thread : threads) { System.out.println(thread); //Thread[main,5,main] } System.out.println("---------"); group.list(); } }
構造方法
- public ThreadGroup(String name):新線程組的父線程組是目前正在運行線程的線程組。
- public ThreadGroup(ThreadGroup parent,String name):新線程組的父線程組是指定的線程組。
得到線程組中的信息
ThreadGroup類中有幾個方法可用來得到線程組中有關線程和子線程的信息
,這些信息包括線程組名字、線程組中可運行線程的數目、線程組中線程的最大優先級、線程組中各線程的名字等:
- int
activeCount()
; //返回此線程組中活動線程的估計數。結果並不能反映併發活動,而且可能受某些系統線程的存在狀態的影響。 - int
activeGroupCount()
; //返回此線程組中活動線程組的估計數。結果並不能反映併發活動。 - int
enumerate(Thread list[])
; //把此線程組及其子組中的全部活動線程複製到指定數組中。 - int
enumerate(Thread[] list, boolean recurse)
; //若是 recurse 標誌爲 true,則還包括對此線程的子組中的全部活動線程的引用。若是數組過小而沒法保持全部線程,則忽略額外的線程。 - int
enumerate(ThreadGroup list[])
; //把對此線程組中的全部活動子組的引用複製到指定數組中。 - int
enumerate(ThreadGroup list[], boolean recurse)
; //若是 recurse 標誌爲 true,則還包括對子組的全部活動子組的引用,等等。 - final int
getMaxPriority()
; //得到當前線程組中最大優先級 - final String
getName()
; //得到當前線程組的名字 - final ThreadGroup
getParent()
; //得到當前線程組的父線程組,頂級線程組僅爲那些父線程組爲 null 的線程組。 - boolean
parentOf(ThreadGroup g)
; //測試此線程組是否爲線程組參數或其祖先線程組之一。 - boolean
isDaemon()
; //若是此線程組是一個後臺程序線程組,則返回 true;不然返回 false。在中止後臺程序線程組的最後一個線程或銷燬其最後一個線程組時,自動銷燬這個後臺程序線程組。 - boolean
isDestroyed()
; //若是此對象被銷燬,則返回 true - void
list()
; //將有關此線程組的信息打印到標準輸出。此方法只對調試有用。 - void
uncaughtException(Thread t, Throwable e)
;//當此線程組中的線程由於一個未捕獲的異常而中止,而且線程沒有安裝特定 Thread.UncaughtExceptionHandler 時,由 Java Virtual Machine 調用此方法。
對線程組操做
ThreadGroup類中的方法都是以線程組爲操做目標的,其中包括設置線程組中線程的最大優先級、將線程組中全部線程掛起或恢復到可運行狀態、終止線程組中全部線程等:
- final void
resume()
; //已過期。使被掛起的當前組內的線程恢復到可運行狀態 - final void
setDaemon(boolean daemon)
; //更改此線程組的後臺程序狀態。 - final void
setMaxPriority(int pri)
; //設置線程組的最高優先級。線程組中已有較高優先級的線程不受影響。 - final void
stop()
;//已過期。終止當前線程組中全部線程 - final void
destroy()
; //銷燬此線程組及其全部子組。此線程組必須爲空,指示此線程組中的全部線程都已中止執行。 - final void
interrupt()
; //中斷此線程組中的全部線程。 - final void
suspend()
; //已過期。掛起當前線程組中全部線程 - final void
checkAccess()
; //肯定當前運行的線程是否有權修改此線程組。 - String
toStrinng()
; //將當前線程組轉換爲String類的對象
2018-5-31