Java基礎16:Java多線程基礎最全總結
Java基礎16:Java多線程基礎最全總結
Java中的線程
Java之父對線程的定義是:
每個程序都至少擁有一個線程-即作為Java虛擬機(JVM)啟動參數運行在主類main方法的線程。在Java虛擬機初始化過程中也可能啟動其他的後臺線程。這種線程的數目和種類因JVM的實現而異。然而所有用戶級線程都是顯式被構造並在主線程或者是其他用戶線程中被啟動。
本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。在這之前,首先讓我們來了解下在操作系統中進程和線程的區別:
?
進程:每個進程都有獨立的代碼和數據空間(進程上下文),進程間的切換會有較大的開銷,一個進程包含1--n個線程。(進程是資源分配的最小單位)
?
線程:同一類線程共享代碼和數據空間,每個線程有獨立的運行棧和程序計數器(PC),線程切換開銷小。(線程是cpu調度的最小單位)
?
線程和進程一樣分為五個階段:創建、就緒、運行、阻塞、終止。
?
多進程是指操作系統能同時運行多個任務(程序)。
?
多線程是指在同一程序中有多個順序流在執行。
?
在java中要想實現多線程,有兩種手段,一種是繼續Thread類,另外一種是實現Runable接口.(其實準確來講,應該有三種,還有一種是實現Callable接口,並與Future、線程池結合使用
Java線程內存模型
下面的圖大致介紹了Java線程的調用過程,每個線程使用一個獨立的調用棧進行線程執行,棧中的數據不共享,堆區和方法區的數據是共享的。
構造方法和守護線程
構造方法
Thread類中不同的構造方法接受如下參數的不同組合:
?
一個Runnable對象,這種情況下,Thread.start方法將會調用對應Runnable對象的run方法。如果沒有提供Runnable對象,那麽就會立即得到一個Thread.run的默認實現。
?
一個作為線程標識名的String字符串,該標識在跟蹤和調試過程中會非常有用,除此別無它用。
?
線程組(ThreadGroup),用來放置新創建的線程,如果提供的ThreadGroup不允許被訪問,那麽就會拋出一個SecurityException 。
?
?
?
Thread對象擁有一個守護(daemon)標識屬性,這個屬性無法在構造方法中被賦值,但是可以在線程啟動之前設置該屬性(通過setDaemon方法)。
?
當程序中所有的非守護線程都已經終止,調用setDaemon方法可能會導致虛擬機粗暴的終止線程並退出。
?
isDaemon方法能夠返回該屬性的值。守護狀態的作用非常有限,即使是後臺線程在程序退出的時候也經常需要做一些清理工作。
?
(daemon的發音為”day-mon”,這是系統編程傳統的遺留,系統守護進程是一個持續運行的進程,比如打印機隊列管理,它總是在系統中運行。)
啟動線程的方式和isAlive方法
啟動線程 調用start方法會觸發Thread實例以一個新的線程啟動其run方法。新線程不會持有調用線程的任何同步鎖。
當一個線程正常地運行結束或者拋出某種未檢測的異常(比如,運行時異常(RuntimeException),錯誤(ERROR) 或者其子類)線程就會終止。
當線程終止之後,是不能被重新啟動的。在同一個Thread上調用多次start方法會拋出InvalidThreadStateException異常。
如果線程已經啟動但是還沒有終止,那麽調用isAlive方法就會返回true.即使線程由於某些原因處於阻塞(Blocked)狀態該方法依然返回true。
如果線程已經被取消(cancelled),那麽調用其isAlive在什麽時候返回false就因各Java虛擬機的實現而異了。沒有方法可以得知一個處於非活動狀態的線程是否已經被啟動過了。
優先級
Java的線程實現基本上都是內核級線程的實現,所以Java線程的具體執行還取決於操作系統的特性。
Java虛擬機為了實現跨平臺(不同的硬件平臺和各種操作系統)的特性,Java語言在線程調度與調度公平性上未作出任何的承諾,甚至都不會嚴格保證線程會被執行。但是Java線程卻支持優先級的方法,這些方法會影響線程的調度:
每個線程都有一個優先級,分布在Thread.MIN_PRIORITY和Thread.MAX_PRIORITY之間(分別為1和10) 默認情況下,新創建的線程都擁有和創建它的線程相同的優先級。main方法所關聯的初始化線程擁有一個默認的優先級,這個優先級是Thread.NORM_PRIORITY (5).
線程的當前優先級可以通過getPriority方法獲得。 線程的優先級可以通過setPriority方法來動態的修改,一個線程的最高優先級由其所在的線程組限定。
線程的控制方法
只有很少幾個方法可以用於跨線程交流:
每個線程都有一個相關的Boolean類型的中斷標識。在線程t上調用t.interrupt會將該線程的中斷標識設為true,除非線程t正處於Object.wait,Thread.sleep,或者Thread.join,這些情況下interrupt調用會導致t上的這些操作拋出InterruptedException異常,但是t的中斷標識會被設為false。
?
任何一個線程的中斷狀態都可以通過調用isInterrupted方法來得到。如果線程已經通過interrupt方法被中斷,這個方法將會返回true。
?
但是如果調用了Thread.interrupted方法且中斷標識還沒有被重置,或者是線程處於wait,sleep,join過程中,調用isInterrupted方法將會拋出InterruptedException異常。
?
調用t.join()方法將會暫停執行調用線程,直到線程t執行完畢:當t.isAlive()方法返回false的時候調用t.join()將會直接返回(return)。
?
另一個帶參數毫秒(millisecond)的join方法在被調用時,如果線程沒能夠在指定的時間內完成,調用線程將重新得到控制權。
?
因為isAlive方法的實現原理,所以在一個還沒有啟動的線程上調用join方法是沒有任何意義的。同樣的,試圖在一個還沒有創建的線程上調用join方法也是不明智的。
?
起初,Thread類還支持一些另外一些控制方法:suspend,resume,stop以及destroy。這幾個方法已經被聲明過期。其中destroy方法從來沒有被實現,估計以後也不會。而通過使用等待/喚醒機制增加suspend和resume方法在安全性和可靠性的效果有所欠缺
Thread的靜態方法
靜態方法
Thread類中的部分方法被設計為只適用於當前正在運行的線程(即調用Thread方法的線程)。為強調這點,這些方法都被聲明為靜態的。
?
Thread.currentThread方法會返回當前線程的引用,得到這個引用可以用來調用其他的非靜態方法,比如Thread.currentThread().getPriority()會返回調用線程的優先級。
?
Thread.interrupted方法會清除當前線程的中斷狀態並返回前一個狀態。(一個線程的中斷狀態是不允許被其他線程清除的)
?
Thread.sleep(long msecs)方法會使得當前線程暫停執行至少msecs毫秒。
?
Thread.yield方法純粹只是建議Java虛擬機對其他已經處於就緒狀態的線程(如果有的話)調度執行,而不是當前線程。最終Java虛擬機如何去實現這種行為就完全看其喜好了。
線程組
每一個線程都是一個線程組中的成員。默認情況下,新建線程和創建它的線程屬於同一個線程組。線程組是以樹狀分布的。
?
當創建一個新的線程組,這個線程組成為當前線程組的子組。getThreadGroup方法會返回當前線程所屬的線程組,對應地,ThreadGroup類也有方法可以得到哪些線程目前屬於這個線程組,比如enumerate方法。
?
ThreadGroup類存在的一個目的是支持安全策略來動態的限制對該組的線程操作。比如對不屬於同一組的線程調用interrupt是不合法的。
?
這是為避免某些問題(比如,一個applet線程嘗試殺掉主屏幕的刷新線程)所采取的措施。ThreadGroup也可以為該組所有線程設置一個最大的線程優先級。
?
線程組往往不會直接在程序中被使用。在大多數的應用中,如果僅僅是為在程序中跟蹤線程對象的分組,那麽普通的集合類(比如java.util.Vector)應是更好的選擇。
多線程的實現
public class 多線程實例 {
?
//繼承thread