本文介紹下線程的3種實現方式並深刻源碼簡單的闡述下原理
複製代碼
Thread類實現了Runnable接口
複製代碼
JVM中的線程必須只能是以上6種狀態的一種。這些狀態是JVM狀態並不能和操做系統線程狀態互相映射
複製代碼
線程剛建立,還未執行(start方法)
複製代碼
已就緒可運行的狀態。 處於此狀態的線程是正在JVM中運行的, 但可能在等待操做系統級別的資源,例如CPU時間片 複製代碼
阻塞等待監視器鎖
處於此狀態的線程正在阻塞等待監視器鎖, 以進入一個同步塊/方法, 或者在執行完wait()方法後重入同步塊/方法 複製代碼
等待 執行完Object.wait無超時參數操做, 或者 Thread.join無超時參數操做(進入等待指定的線程執行結束), 或者 LockSupport.park操做後,線程進入等待狀態。 通常在等待狀態的線程在等待其它線程執行特殊操做, 例如:等待另其它線程操做Object.notify()喚醒或者Object.notifyAll()喚醒全部。 複製代碼
限時等待 Thread.sleep、Object.wait帶超時時間、 Thread.join帶超時時間、 LockSupport.parkNanos、 LockSupport.parkUntil這些操做會時線程進入限時等待 複製代碼
終止,線程執行完畢
複製代碼
JVM 線程狀態流轉圖java
注意:不要混淆操做系統線程狀態和java線程狀態。JVM中的線程必須只能是以上6種狀態的一種!RUNNABLE = 正在JVM中運行的(Running)+ 可能在等待操做系統級別的資源(Ready)例如CPU時間片web
線程建立以後,不會當即進入就緒狀態,由於線程的運行須要一些條件(好比內存資源),編程
只有線程運行須要的全部條件知足了,才進入就緒狀態。緩存
當線程進入就緒狀態後,不表明馬上就能獲取CPU執行時間,也許此時CPU正在執行其餘的事情,所以它要等待。安全
當獲得CPU執行時間以後,線程便真正進入運行狀態。多線程
線程在運行狀態過程當中,可能有多個緣由致使當前線程不繼續運行下去,好比用戶主動讓線程睡眠(睡眠必定的時間以後再從新執行)、用戶主動讓線程等待,或者被同步塊給阻塞,此時就對應着多個狀態:time waiting(睡眠或等待必定的事件)、waiting(等待被喚醒)、blocked(阻塞)。編輯器
當因爲忽然中斷或者子任務執行完畢,線程就會被消亡。函數式編程
表示Thread的名字, 能夠經過Thread類的構造器中的參數來指定線程名字 複製代碼
線程的優先級(最大值爲10,最小值爲1,默認值爲5)
複製代碼
線程是不是守護線程,若是在main線程中建立了一個守護線程,當main方法運行完畢以後,守護線程也會隨着消亡。在JVM中,垃圾收集器線程就是守護線程
複製代碼
要執行的任務
複製代碼
線程羣組
複製代碼
start()用來啓動一個線程,當調用start方法後,系統纔會開啓一個新的線程來執行用戶定義的子任務,在這個過程當中,會爲相應的線程分配須要的資源
複製代碼
run()方法是不須要用戶來調用的,當經過start方法啓動一個線程以後,當線程得到了CPU執行時間,便進入run方法體去執行具體的任務。注意,繼承Thread類必須重寫run方法,在run方法中定義具體要執行的任務
複製代碼
有2個重載版本函數
public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException; 複製代碼
sleep讓線程睡眠,交出CPU,讓CPU去執行其餘的任務。 sleep方法不會釋放鎖,也就是說若是當前線程持有對某個對象的鎖, 則即便調用sleep方法,其餘線程也沒法訪問這個對象。 sleep方法至關於讓線程進入阻塞狀態。 複製代碼
調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其餘的線程。 它跟sleep方法相似,一樣不會釋放鎖。 可是yield不能控制具體的交出CPU的時間, 另外,yield方法只能讓擁有相同優先級的線程有獲取CPU執行時間的機會。 注意,調用yield方法並不會讓線程進入阻塞狀態, 而是讓線程重回就緒狀態,它只須要等待從新獲取CPU執行時間, 這一點是和sleep方法不同的。 複製代碼
有三個重載版本flex
join() join(long millis) //參數爲毫秒 join(long millis,int nanoseconds) //第一參數爲毫秒,第二個參數爲納秒 能夠看出,當調用thread.join()方法後,main線程會進入等待,而後等待thread執行完以後再繼續執行 複製代碼
join方法實際上調用的是Object的wait方法
wait方法會讓線程進入阻塞狀態,而且會釋放線程佔有的鎖,並交出CPU執行權限。 複製代碼
nterrupt,中斷。 單獨調用interrupt方法可使得處於阻塞狀態的線程拋出一個異常, 也就說,它能夠用來中斷一個正處於阻塞狀態的線程 複製代碼
stop方法已是一個廢棄的方法,它是一個不安全的方法。 由於調用stop方法會直接終止run方法的調用,而且會拋出一個ThreadDeath錯誤, 若是線程持有某個對象鎖的話,會徹底釋放鎖,致使對象狀態不一致。 因此stop方法基本是不會被用到的。 複製代碼
該註解不是必須的,若是一個接口符合"函數式接口"定義,那麼加不加該註解都沒有影響。
加上該註解可以更好地讓編譯器進行檢查。 若是編寫的不是函數式接口,可是加上了@FunctionInterface,那麼編譯器會報錯 複製代碼
// 正確的函數式接口
@FunctionalInterface public interface TestInterface { // 抽象方法 public void sub(); // java.lang.Object中的public方法 public boolean equals(Object var1); // 默認方法 public default void defaultMethod(){ } // 靜態方法 public static void staticMethod(){ } } // 錯誤的函數式接口(有多個抽象方法) @FunctionalInterface public interface TestInterface2 { void add(); void sub(); } 複製代碼
目的是讓接口能夠擁有具體的方法,讓接口內部包含了一些默認的方法實現 若是一個類、類屬變量及方法沒有用任何修飾符 (即沒有用public、protected及private中任何一種修飾) 則其訪問權限爲default(默認訪問權限) 複製代碼
取消任務 參數:是否當即中斷任務執行,或者等等任務結束 複製代碼
任務是否已經取消,若已取消,返回true
若計算尚未開始,它被取消且再也不開始。 若計算處於運行之中,那麼若是mayInterrupt參數爲true,它就被中斷。 複製代碼
任務是否已經完成。包括任務正常完成、拋出異常或被取消,都返回true
複製代碼
等待任務執行結束,得到V類型的結果。 InterruptedException: 線程被中斷異常, ExecutionException: 任務執行異常, 若是任務被取消,還會拋出CancellationException 複製代碼
參數timeout指定超時時間,uint指定時間的單位,在枚舉類TimeUnit中有相關的定義。 若是計算超時,將拋出TimeoutException 設置了超時時間能夠防止程序無限制的等待future的返回結果 複製代碼
Executor類族是基於生產者-消費者思想設計的, 提交任務至關於生產者生產任務, 而執行任務至關於消費者消費任務 複製代碼
繼承Executor 添加了一些用於生命週期管理的方法
複製代碼
將執行平緩的關閉過程:再也不接受新的任務,而且等待已經提交的任務完成
複製代碼
表示當即關閉
複製代碼
判斷是否關閉或者終止
複製代碼
用於提交的任務,能夠用Runnable做爲任務,也能夠用Callable做爲任務
複製代碼
用於建立線程池
複製代碼
一、newFixedThreadPool
固定長度線程池 每當提交任務建立一個線程,直到線程池的最大數量, 該方法返回的是ExecutorService對象,能夠看出該方法調用了ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()),其中 第一個參數表示核心線程數,即初始化建立的線程數; 第二個參數是若是最大線程數,即當任務比較多的時候,會自動增長線程數,但不得超過該值; 第三個參數表示等待時間,當超過該時間線程沒收到任務則會收回線程; 第四個參數是第三個參數的單位; 最後一個參數是一個阻塞隊列,用於存放工做任務 複製代碼
二、newSingleThreadExecutor
單線程,這裏調用了Executors工廠類的靜態方法newSingleThreadExecutor, 建立ThreadPoolExecutor對象時,將線程數限制在1便可。 其它如固定長度線程池,只不過是最多隻有一個線程工做 複製代碼
三、newCachedThreadPool
建立一個可緩存的線程池, 若是線程池規模過大,回收空閒線程, 若是需求增長,則添加新的線程。 返回的是 new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>()); 從返回的對象能夠看出,線程初始值是0,最大值是Integer.MAX_VALUE,因此可以使得線程數是動態變化的。 複製代碼
四、newScheduledThreadPool
建立固定長度的線程池,以延遲或定時的方式來執行任務,返回ScheduledThreadPoolExecutor對象
複製代碼
經過以上四種靜態方法構造的線程池中,主要涉及到兩個線程池類,一個是ScheduledThreadPoolExecutor,一個是ThreadPoolExecutor
a、ScheduledThreadPoolExecutor
b、ThreadPoolExecutor
Executor爲了解決多線程任務調度的問題,能夠合理平衡系統吞吐量與內存管理。 Executor是一個最頂層的接口,爲了更好管理Executor的生命週期, 便擴展了ExceutorService接口,該類有具體實現類ThreadPoolExecutor和ScheduledExecutorService。 並經過工廠類Executors的幾個靜態方法來建立不一樣功用的線程池 複製代碼
本文使用 mdnice 排版