快速了解多線程的一些常見知識點
在Java編程中,多線程是非常重要知識點. 關(guān)于Java線程有些不可不知的知識點需要牢記,下面就介紹了這些知識點.
1. 優(yōu)先級
每個Java線程都有對應(yīng)的優(yōu)先級,高優(yōu)先級的線程比低優(yōu)先級的線程有更多的執(zhí)行機(jī)會。新創(chuàng)建的Java線程默認(rèn)的優(yōu)先級是5, 最低是1,最高是10;
1 | /** |
Note:如果在一個線程中創(chuàng)建一個新的線程,新線程的默認(rèn)優(yōu)先級和所在線程的優(yōu)先級保持一致。
2. daemon 或 非daemon
一個Java線程可以是daemon的也可以是非daemon的,JVM在啟動的時候啟動了main線程,該線程是非daemon;JVM在下面2的情況下會退回:
調(diào)用
Runtime.exit()方法所有非daemon的線程都結(jié)束了。
3. 創(chuàng)建線程的方式
有2種創(chuàng)建線程的方式,一種是繼承Thread類;一種是直接new一個Thread對象,并在構(gòu)造函數(shù)中傳入一個Runnable對象。
繼承
1 | class PrimeThread extends Thread { |
4. 線程的狀態(tài)
JDK中關(guān)于線程狀態(tài)的定義:
new : 一個新創(chuàng)建的,沒有調(diào)用start方法的線程處于該狀態(tài);
RUNNABLE :可執(zhí)行的,在JVM中是處于執(zhí)行中,但是在等待其它的操作系統(tǒng)資源,如cpu資源;
BLOCKED :阻塞的,一個線程在進(jìn)入(或再次進(jìn)入)一個同步方法或同步塊時等待監(jiān)視器鎖時處于該狀態(tài)。
WAITING :等待,一個線程在調(diào)用了如下的方法時會處于等待狀態(tài):
1.Object.wait
2.Thread.join //正在主線程中調(diào)用一個一個線程的join方法,在主線程處于等待狀態(tài)
3.LockSupport#park()TIMED_WAITING 等待的,不過該等待狀態(tài)是有時間限制的。一個線程調(diào)用下面的方法會進(jìn)入該狀態(tài):
1.Thread.sleep
2.Object.wait
3.Thread.join
4.LockSupport.parkNanos
5.LockSupport.parkUntilTERMINATED 終止,當(dāng)一個線程執(zhí)行完成后處于該狀態(tài)
5. 線程的調(diào)度
理想的情況下,每個程序的線程都擁有一個專屬于自己的處理器;在計算機(jī)還不能擁有幾千,甚至幾百萬CPU處理器的情況下,多個線下需要共享僅有的cpu資源,如果分配線程執(zhí)行所需的cpu資源就需要線程調(diào)度器的調(diào)度了。在操作系統(tǒng)層面有自己的線程調(diào)度器,在JVM中也存在Java線程調(diào)度器。
在Java線程調(diào)度中有2點比較重要:
Java規(guī)范并沒有強制要求每個JVM按照特定的調(diào)度規(guī)則調(diào)用線程,或者必須包含一個線程調(diào)度器。線程調(diào)度的實現(xiàn)完全是依賴平臺的。
在編寫Java多線程代碼時,我們唯一需要考慮的是不要讓一個線程大量的占用cpu時間(eg.死循環(huán))。
大多數(shù)平臺上JVM的線程調(diào)度是依賴操作系統(tǒng)本身的線程調(diào)度器的,每個線下有不同的優(yōu)先級,在基于時間片的規(guī)則下,高優(yōu)先級的線程擁有更多的CPU執(zhí)行機(jī)會;相同優(yōu)先級的線程可以按照FIFO調(diào)度。
6. Runnable、Callable、Future 和 FutureTasek的區(qū)別
Java中存在Runnable、Callable、Future、FutureTask這幾個與線程相關(guān)的類或者接口,在Java中也是比較重要的幾個概念,我們通過下面的簡單示例來了解一下它們的作用與區(qū)別。
6.1 Runnable
Runnable 應(yīng)該是這幾個類我們使用的最多的一個。JDK的文檔說明:如果一個類的實例想要通過一個線程來執(zhí)行,則該類應(yīng)該實現(xiàn)Runnable接口。Runnable被設(shè)計用來對那些處于active狀態(tài)時會執(zhí)行代碼的對象提供一個統(tǒng)一的協(xié)議。例如Thread類就實現(xiàn)了Runnable接口。
1 | public interface Runnable { |
6.2 Callable
Callable 表示一個可以攜帶返回結(jié)果的任務(wù)。該接口的實現(xiàn)類需要一個沒有參數(shù)的call方法。Callable 和 Runnable類似,都是被設(shè)計用來被另外一個線程執(zhí)行的任務(wù)。但是Runnable不能返回一個結(jié)果,且不能拋出一個checked異常。
1 | public interface Callable<V> { |
6.3 Future
一個Futrue表示一個異步計算的結(jié)果。它提供了一系列方法,用來檢測計算是否完成,獲取計算結(jié)果,等待計算結(jié)果等。只能通過get方法獲取計算結(jié)果;如果計算沒有完成,在必要條件下,則get方法一直等待,直到任務(wù)完成。cancel方法用來取消任務(wù)。其它的方法都是用來測試計算是否完成,或是否取消。一旦計算完成后,就不可以被取消。如果你想使用Future,但并不需要返回一個結(jié)果,則可以使用Future>并返回null。
一個簡單的例子(JDK原文):
1 | class App { |
6.4 FutureTask
FutureTask 表示一個可以取消的異步計算任務(wù)。它實現(xiàn)了Runnable接口和Future接口。FutureTask 可以用來包裝Runnable和Callable對象。應(yīng)該FutureTask實現(xiàn)了Runnable接口。同時可以通過被提交到Executor去執(zhí)行。
1 | public FutureTask(Callable |
可以看到,Runnable注入會被Executors.callable()函數(shù)轉(zhuǎn)換為Callable類型,即FutureTask最終都是執(zhí)行Callable類型的任務(wù)。該適配函數(shù)的實現(xiàn)如下 :
1 | public static |
RunnableAdapter適配器
1 | /** |
7. 總結(jié)
由于FutureTask實現(xiàn)了Runnable,因此它既可以通過Thread包裝來直接執(zhí)行,也可以提交給ExecuteService來執(zhí)行。
并且還可以直接通過get()函數(shù)獲取執(zhí)行結(jié)果,該函數(shù)會阻塞,直到結(jié)果返回。因此FutureTask既是Future、
Runnable,又是包裝了Callable(如果是Runnable最終也會被轉(zhuǎn)換為Callable ), 它是這兩者的合體。
source: //leokongwq.github.io/2016/10/16/java-runnable-callable-future.html

喜歡,在看
