第1章 并發(fā)編程的挑戰(zhàn)
vmstat 可以測量上下文切換的次數(shù)
Lmbench3 可以測量上下文切換的時長。
jstack 查看線程狀態(tài)
第2章 Java并發(fā)機制的底層實現(xiàn)原理
1. volatile——下文討論
2. synchronized
- 1.對于普通同步方法跪帝,鎖是當前實例對象。
- 2.對于靜態(tài)同步方法回论,鎖是當前類的Class對象蹦漠。
- 3.對于同步方法塊,鎖是Synchonized括號里配置的對象雨涛。
任何對象都有一個monitor與之關(guān)聯(lián),當且一個monitor被持有后懦胞,它將處于鎖定狀態(tài)替久。
synchronized關(guān)鍵字中,JVM基于進入和退出Monitor對象來實現(xiàn)鎖躏尉。代碼塊同步是使用monitorenter和monitorexit指令實現(xiàn)的侣肄,而方法同步是使用另外一種方式實現(xiàn)的,細節(jié)在JVM規(guī)范里并沒有詳細說明醇份。
synchronized用的鎖是存在Java對象頭里的稼锅。
3. 鎖一共有3種:偏向鎖、輕量級鎖和重量級鎖
偏向鎖:大多數(shù)情況下僚纷,鎖不僅不存在多線程競爭矩距,由同一線程多次獲得,為了讓線程獲得鎖的代價更低而引入了偏向鎖怖竭。
輕量級鎖:加鎖如果成功锥债,當前線程獲得鎖,如果失敗痊臭,表示其他線程競爭鎖哮肚,當前線程便嘗試使用自旋來獲取鎖。解鎖如果失敗广匙,表示當前鎖存在競爭允趟,鎖就會膨脹成重量級鎖。
一旦鎖升級成重量級鎖鸦致,就不會再恢復到輕量級鎖狀態(tài)潮剪。
4. CAS實現(xiàn)原子操作的三大問題
- ABA問題
- 循環(huán)時間長開銷大
- 只能保證一個共享變量的原子操作
第3章 Java內(nèi)存模型
在并發(fā)編程中,需要處理兩個關(guān)鍵問題:線程之間如何通信及線程之間如何同步分唾。在命令式編程中抗碰,線程之間的通信機制有兩種:1)共享內(nèi)存 、2)消息傳遞绽乔。
在共享內(nèi)存的并發(fā)模型里弧蝇,線程之間共享程序的公共狀態(tài),通過寫-讀內(nèi)存中的公共狀態(tài)進行隱式通信。在消息傳遞的并發(fā)模型里看疗,線程之間沒有公共狀態(tài)沙峻,線程之間必須通過發(fā)送消息來顯式進行通信。
同步是指程序中用于控制不同線程間操作發(fā)生相對順序的機制鹃觉。在共享內(nèi)存并發(fā)模型里专酗,同步是顯式進行的睹逃。程序員必須顯式指定某個方法或某段代碼需要在線程之間互斥執(zhí)行盗扇。在消息傳遞的并發(fā)模型里,由于消息的發(fā)送必須在消息的接收之前沉填,因此同步是隱式進行的疗隶。
Java的并發(fā)采用的是共享內(nèi)存模型,Java線程之間的通信總是隱式進行翼闹,整個通信過程對程序員完全透明斑鼻。編程時很可能會遇到各種奇怪的內(nèi)存可見性問題。
在Java中猎荠,所有實例域坚弱、靜態(tài)域和數(shù)組元素都存儲在堆內(nèi)存中,堆內(nèi)存在線程之間共享关摇。
局部變量荒叶,方法定義參數(shù)和異常處理器參數(shù)不會在線程之間共享,它們不會有內(nèi)存可見性問題输虱,也不受內(nèi)存模型的影響些楣。
JMM定義了線程和主內(nèi)存之間的抽象關(guān)系
線程之間的共享變量存儲在主內(nèi)存中,每個線程都有一個私有的本地內(nèi)存宪睹,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本愁茁。
本地內(nèi)存是JMM的一個抽象概念,并不真實存在亭病。它涵蓋了緩存鹅很、寫緩沖區(qū)、寄存器以及其他的硬件和編譯器優(yōu)化罪帖。
如果線程A與線程B之間要通信的話道宅,必須要經(jīng)歷下面2個步驟。
1. 線程A把本地內(nèi)存A中更新過的共享變量刷新到主內(nèi)存中去胸蛛。
2. 線程B到主內(nèi)存中去讀取線程A之前已更新過的共享變量污茵。
為了提高性能,編譯器和處理器常常會對指令做重排序葬项。重排序分3種類型泞当。
為了保證內(nèi)存可見性,Java編譯器在生成指令序列的適當位置會插入內(nèi)存屏障指令來禁止特定類型的處理器重排序民珍。JMM把內(nèi)存屏障指令分為4類襟士。
happens-before規(guī)則:
數(shù)據(jù)依賴性
如果兩個操作訪問同一個變量盗飒,且這兩個操作中有一個為寫操作,此時這兩個操作之間就存在數(shù)據(jù)依賴性陋桂。
僅針對單個處理器中執(zhí)行的指令序列和單個線程中執(zhí)行的操作逆趣,編譯器和處理器在重排序時,會遵守數(shù)據(jù)依賴性嗜历。
但是不同處理器之間和不同線程之間的數(shù)據(jù)依賴性不被編譯器和處理器考慮宣渗。
as-if-serial語義使得單線程程序是按程序的順序來執(zhí)行的。as-if-serial語義使單線程程序員無需擔心重排序會干擾他們梨州,也無需擔心內(nèi)存可見性問題痕囱。
順序一致性——即程序的執(zhí)行結(jié)果與該程序在順序一致性內(nèi)存模型中的執(zhí)行結(jié)果相同。
順序一致性內(nèi)存模型的視圖:
順序一致性模型中暴匠,所有操作完全按程序的順序串行執(zhí)行鞍恢。而在JMM中,臨界區(qū)內(nèi)的代碼可以重排序每窖。
JMM不保證未同步程序的執(zhí)行結(jié)果與該程序在順序一致性模型中的執(zhí)行結(jié)果一致帮掉。
JMM不保證對64位的long型和double型變量的寫操作具有原子性。
volatile的特性
即使是64位的long型和double型變量窒典,只要它是volatile變量蟆炊,對該變量的讀/寫就具有原子性。
如果是多個volatile操作或類似于volatile++這種復合操作崇败,這些操作整體上不具有原子性盅称。
volatile有:內(nèi)存可見性、順序一致性后室、但不具有原子性缩膝。
從內(nèi)存語義的角度來說,volatile的寫-讀與鎖的釋放-獲取有相同的內(nèi)存效果:volatile寫和鎖的釋放有相同的內(nèi)存語義岸霹;volatile讀與鎖的獲取有相同的內(nèi)存語義疾层。
當寫一個volatile變量時,JMM會把該線程對應的本地內(nèi)存中的共享變量值刷新到主內(nèi)
存贡避。
當讀一個volatile變量時痛黎,JMM會把該線程對應的本地內(nèi)存置為無效。線程接下來將從主
內(nèi)存中讀取共享變量刮吧。(不緩存)
總結(jié):
線程A寫一個volatile變量湖饱,實質(zhì)上是線程A向接下來將要讀這個volatile變量的某個線程發(fā)出了(其對共享變量所做修改的)消息。
線程B讀一個volatile變量杀捻,實質(zhì)上是線程B接收了之前某個線程發(fā)出的(在寫這個volatile變量之前對共享變量所做修改的)消息井厌。
線程A寫一個volatile變量,隨后線程B讀這個volatile變量,這個過程實質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息仅仆。
volatile重排序規(guī)則表:
JMM內(nèi)存屏障插入策:
在每個volatile寫操作的前面插入一個StoreStore屏障器赞。
在每個volatile寫操作的后面插入一個StoreLoad屏障。
在每個volatile讀操作的后面插入一個LoadLoad屏障墓拜。
在每個volatile讀操作的后面插入一個LoadStore屏障港柜。
過多地使用volatile是不必要的,因為它會降低程序執(zhí)行的效率咳榜。
鎖釋放和鎖獲取的內(nèi)存語義做個總結(jié)夏醉。
線程A釋放一個鎖,實質(zhì)上是線程A向接下來將要獲取這個鎖的某個線程發(fā)出了(線程A對共享變量所做修改的)消息贿衍。
線程B獲取一個鎖授舟,實質(zhì)上是線程B接收了之前某個線程發(fā)出的(在釋放這個鎖之前對共享變量所做修改的)消息救恨。
線程A釋放鎖贸辈,隨后線程B獲取這個鎖,這個過程實質(zhì)上是線程A通過主內(nèi)存向線程B發(fā)送消息肠槽。
ReentrantLock的部分類圖
ReentrantLock分為公平鎖和非公平鎖:默認非公平鎖擎淤。
concurrent包的實現(xiàn)
有一個通用化的實現(xiàn)模式:
首先,聲明共享變量為volatile秸仙。
然后嘴拢,使用CAS的原子條件更新來實現(xiàn)線程之間的同步。
同時寂纪,配合以volatile的讀/寫和CAS所具有的volatile讀和寫的內(nèi)存語義來實現(xiàn)線程之間的通信席吴。
concurrent包的實現(xiàn)示意圖
final域的內(nèi)存語義——沒有很深的理解
1)JMM禁止編譯器把final域的寫重排序到構(gòu)造函數(shù)之外。
2)編譯器會在final域的寫之后捞蛋,構(gòu)造函數(shù)return之前孝冒,插入一個StoreStore屏障。這個屏障禁止處理器把final域的寫重排序到構(gòu)造函數(shù)之外拟杉。
JMM對這兩種不同性質(zhì)的重排序庄涡,采取了不同的策略:
對于會改變程序執(zhí)行結(jié)果的重排序,JMM要求編譯器和處理器必須禁止這種重排序搬设。
對于不會改變程序執(zhí)行結(jié)果的重排序穴店,JMM對編譯器和處理器不做要求(JMM允許這種重排序)。
happens-before和as-if-serial語義
as-if-serial語義保證單線程內(nèi)程序的執(zhí)行結(jié)果不被改變拿穴,happens-before關(guān)系保證正確同
步的多線程程序的執(zhí)行結(jié)果不被改變泣洞。
第4章 Java并發(fā)編程基礎
Java線程的狀態(tài)
線程創(chuàng)建之后,調(diào)用start()方法開始運行默色。執(zhí)行Runnable的run()方法之后將會進入到終止狀態(tài)球凰。
線程間通信
等待/通知的相關(guān)方法
管道輸入/輸出流
管道輸入/輸出流和普通的文件輸入/輸出流或者網(wǎng)絡輸入/輸出流不同之處在于,它主要用于線程之間的數(shù)據(jù)傳輸,而傳輸?shù)拿浇闉閮?nèi)存弟蚀。
線程A執(zhí)行thread.join()語句:線程A等待thread線程終止之后才從thread.join()蚤霞。
第5章 Java中的鎖
Lock接口
鎖獲取與釋放的可操作性、可中斷的獲取鎖以及超時獲取鎖特性义钉。
Lock lock = new ReentrantLock();
lock.lock();
try {
} finally {
lock.unlock();
}
Lock接口提供的synchronized關(guān)鍵字不具備的主要特性
隊列同步器AbstractQueuedSynchronizer(簡稱同步器)
鎖是面向使用者的昧绣,它定義了使用者與鎖交互的接口(比如可以允許兩個線程并行訪問),隱藏了實現(xiàn)細節(jié)捶闸。
同步器面向的是鎖的實現(xiàn)者夜畴,它簡化了鎖的實現(xiàn)方式,屏蔽了同步狀態(tài)管理删壮、線程的排隊贪绘、等待與喚醒等底層操作。
需要使用同步器提供的如下3個方法來訪問或修改同步狀態(tài)央碟。
getState():獲取當前同步狀態(tài)税灌。
setState(int newState):設置當前同步狀態(tài)。
compareAndSetState(int expect,int update):使用CAS設置當前狀態(tài)亿虽,該方法能夠保證狀態(tài)設置的原子性菱涤。
同步器提供的模板方法
同步器提供的模板方法基本上分為3類:
1. 獨占式獲取與釋放同步狀態(tài)
2. 共享式獲取與釋放同步狀態(tài)
3. 查詢同步隊列中的等待線程情況
同步器依賴內(nèi)部的同步隊列(一個FIFO雙向隊列)來完成同步狀態(tài)的管理
同步隊列中的節(jié)點(Node)用來保存獲取同步狀態(tài)失敗的線程引用、等待狀態(tài)以及前驅(qū)和后繼節(jié)點
獨占式同步狀態(tài)獲取流程洛勉,也就是acquire(int arg)方法調(diào)用流程
共享式與獨占式訪問資源的對比
獨占式超時獲取同步狀態(tài)的流程
ReentrantLock(可重入鎖)
公平性:如果在絕對時間上粘秆,先對鎖進行獲取的請求一定先被滿足,那么這個鎖是公平的收毫,反之攻走,是不公平的。
非公平性鎖雖然可能造成線程“饑餓”此再,但極少的線程切換昔搂,保證了其更大的吞吐量。
讀寫鎖ReentrantReadWriteLock
而讀寫鎖在同一時刻可以允許多個讀線程訪問,但是在寫線程訪問時,所有的讀線程和其他寫線程均被阻塞鸣皂。讀寫鎖維護了一對鎖止邮,一個讀鎖和一個寫鎖,通過分離讀鎖和寫鎖,使得并發(fā)性相比一般的排他鎖有了很大提升。
LockSupport工具類: 阻塞或喚醒一個線程
LockSupport提供的阻塞和喚醒方法
Condition接口 (內(nèi)部類)
Condition定義了等待/通知兩種類型的方法,當前線程調(diào)用這些方法時别凹,需要提前獲取到Condition對象關(guān)聯(lián)的鎖。Condition對象是由Lock對象(調(diào)用Lock對象的newCondition()方法)創(chuàng)
建出來的洽糟,換句話說炉菲,Condition是依賴Lock對象的堕战。
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void conditionWait() throws InterruptedException {
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
} public void conditionSignal() throws InterruptedException {
lock.lock();
try {
condition.signal();
} finally {
lock.unlock();
}
}
每個Condition對象都包含著一個隊列(以下稱為等待隊列),該隊列是Condition對象實現(xiàn)等待/通知功能的關(guān)鍵拍霜。
同步隊列與等待隊列
Condition的實現(xiàn)是同步器的內(nèi)部類嘱丢,因此每個Condition實例都能夠訪問同步器提供的方法,相當于每個Condition都擁有所屬同步器的引用祠饺。
調(diào)用Condition的await()方法(或者以await開頭的方法)越驻,會使當前線程進入等待隊列并釋放鎖,同時線程狀態(tài)變?yōu)榈却隣顟B(tài)道偷。
調(diào)用Condition的signal()方法缀旁,將會喚醒在等待隊列中等待時間最長的節(jié)點(首節(jié)點),在喚醒節(jié)點之前勺鸦,會將節(jié)點移到同步隊列中并巍。
Condition的signalAll()方法,相當于對等待隊列中的每個節(jié)點均執(zhí)行一次signal()方法换途,效
果就是將等待隊列中所有節(jié)點全部移動到同步隊列中懊渡,并喚醒每個節(jié)點的線程。
第6章 Java并發(fā)容器和框架
ConcurrentHashMap的實現(xiàn)原理與使用
在并發(fā)編程中使用HashMap可能導致程序死循環(huán)怀跛。而使用線程安全的HashTable效率又非常低下距贷,基于以上兩個原因柄冲,便有了ConcurrentHashMap的登場機會吻谋。
在多線程環(huán)境下,使用HashMap進行put操作會引起死循環(huán)现横,導致CPU利用率接近100%漓拾。這是因為多線程會導致HashMap的Entry鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu),一旦形成環(huán)形數(shù)據(jù)結(jié)構(gòu)戒祠,Entry的next節(jié)點永遠不為空骇两,就會產(chǎn)生死循環(huán)獲取Entry。
HashTable容器使用synchronized來保證線程安全姜盈,但在線程競爭激烈的情況下HashTable的效率非常低下低千。
ConcurrentHashMap的鎖分段技術(shù)可有效提升并發(fā)訪問率
ConcurrentHashMap的類圖
ConcurrentHashMap的結(jié)構(gòu)圖
segments數(shù)組的長度是2的N次方,假如concurrencyLevel等于14馏颂、15或16示血,ssize都會等于16,即容器里鎖的個數(shù)也是16救拉。
segments最大值是65535难审,在默認情況下segments等于16。
變量cap就是segment里HashEntry數(shù)組的長度亿絮,loadfactor默認等于0.75告喊。
Segment的get操作麸拄、put操作和size操作。
get:先經(jīng)過一次再散列黔姜,然后使用這個散列值通過散列運算定位到Segment拢切,再通過散列算法定位到元素。
set:插入操作需要經(jīng)歷兩個步驟秆吵,第一步判斷是否需要對Segment里的HashEntry數(shù)組進行擴容失球,第二步定位添加元素的位置,然后將其放在HashEntry數(shù)組里帮毁。
值得一提的是实苞,Segment的擴容判斷比HashMap更恰當,因為HashMap是在插入元素后判斷元素是否已經(jīng)到達容量的烈疚,如果到達了就進行擴容黔牵,但是很有可能擴容之后沒有新元素插入,這時HashMap就進行了一次無效的擴容爷肝。
在擴容的時候猾浦,首先會創(chuàng)建一個容量是原來容量兩倍的數(shù)組,然后將原數(shù)組里的元素進行再散列后插入到新的數(shù)組里灯抛。為了高效金赦,ConcurrentHashMap不會對整個容器進行擴容,而只對某個segment進行擴容对嚼。
ConcurrentLinkedQueue的結(jié)構(gòu):之后用到在做詳細了解夹抗。
JDK 7提供了7個阻塞隊列
ArrayBlockingQueue:一個由數(shù)組結(jié)構(gòu)組成的有界阻塞隊列。
LinkedBlockingQueue:一個由鏈表結(jié)構(gòu)組成的有界阻塞隊列纵竖。
PriorityBlockingQueue:一個支持優(yōu)先級排序的無界阻塞隊列漠烧。
DelayQueue:一個使用優(yōu)先級隊列實現(xiàn)的無界阻塞隊列。
SynchronousQueue:一個不存儲元素的阻塞隊列靡砌。
LinkedTransferQueue:一個由鏈表結(jié)構(gòu)組成的無界阻塞隊列已脓。
LinkedBlockingDeque:一個由鏈表結(jié)構(gòu)組成的雙向阻塞隊列。
Fork/Join框架:類似歸并排序通殃,且為工作竊取模式度液,可以參看之前的筆記。
第7章 Java中的13個原子操作類
java.util.concurrent.atomic
使用方法基本一致画舌,具體的使用到的時候堕担,參看API。
第8章 Java中的并發(fā)工具類
等待多線程完成的CountDownLatch
CountDownLatch允許一個或多個線程等待其他線程完成操作骗炉。
public class CountDownLatchTest {
staticCountDownLatch c = new CountDownLatch(2);
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(1);
c.countDown();
System.out.println(2);
c.countDown();
}
}).start();
c.await();
System.out.println("3");
}
}
同步屏障CyclicBarrier
它要做的事情是照宝,讓一組線程到達一個屏障(也可以叫同步點)時被阻塞,直到最后一個線程到達屏障時句葵,屏障才會開門厕鹃,所有被屏障攔截的線程才會繼續(xù)運行兢仰。
public class CyclicBarrierTest {
staticCyclicBarrier c = new CyclicBarrier(2);
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
c.await();
} catch (Exception e) {
}
System.out.println(1);
}
}).start();
try {
c.await();
} catch (Exception e) {
}
System.out.println(2);
}
}
CyclicBarrier可以用于多線程計算數(shù)據(jù),最后合并計算結(jié)果的場景剂碴。
CountDownLatch的計數(shù)器只能使用一次把将,而CyclicBarrier的計數(shù)器可以使用reset()方法重置。所以CyclicBarrier能處理更為復雜的業(yè)務場景忆矛。例如察蹲,如果計算發(fā)生錯誤,可以重置計數(shù)器催训,并讓線程重新執(zhí)行一次洽议。
控制并發(fā)線程數(shù)的Semaphore
Semaphore可以用于做流量控制
在一些列的線程中,只允許多少個可以并發(fā)運行漫拭。
Exchanger 線程間交換數(shù)據(jù)
Exchanger:提供一個同步點亚兄,在這個同步點,兩個線程可以交換彼此的數(shù)據(jù)采驻。這兩個線程通過exchange方法交換數(shù)據(jù)审胚,如果第一個線程先執(zhí)行exchange()方法,它會一直等待第二個線程也執(zhí)行exchange方法礼旅,當兩個線程都到達同步點時膳叨,這兩個線程就可以交換數(shù)據(jù),將本線程生產(chǎn)出來的數(shù)據(jù)傳遞給對方痘系。
第9章 Java中的線程池
ThreadPoolExecutor執(zhí)行execute()方法的示意圖
ThreadPoolExecutor執(zhí)行示意圖
1)如果當前運行的線程少于corePoolSize菲嘴,則創(chuàng)建新線程來執(zhí)行任務(注意,執(zhí)行這一步驟需要獲取全局鎖)碎浇。
2)如果運行的線程等于或多于corePoolSize临谱,則將任務加入BlockingQueue。
3)如果無法將任務加入BlockingQueue(隊列已滿)奴璃,則創(chuàng)建新的線程來處理任務(注意,執(zhí)行這一步驟需要獲取全局鎖)城豁。
4)如果創(chuàng)建新線程將使當前運行的線程超出maximumPoolSize苟穆,任務將被拒絕,并調(diào)用RejectedExecutionHandler.rejectedExecution()方法唱星。
ThreadPoolExecutor采取上述步驟的總體設計思路雳旅,是為了在執(zhí)行execute()方法時,盡可能地避免獲取全局鎖(那將會是一個嚴重的可伸縮瓶頸)间聊。在ThreadPoolExecutor完成預熱之后(當前運行的線程數(shù)大于等于corePoolSize)攒盈,幾乎所有的execute()方法調(diào)用都是執(zhí)行步驟2,而步驟2不需要獲取全局鎖哎榴。
創(chuàng)建一個線程池時需要輸入幾個參數(shù)
1 corePoolSize(線程池的基本大行突怼)
2 runnableTaskQueue(任務隊列)阻塞隊列
ArrayBlockingQueue:
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue
3 maximumPoolSize(線程池最大數(shù)量)
4 ThreadFactory:用于設置創(chuàng)建線程的工廠
5 RejectedExecutionHandler(飽和策略)
AbortPolicy:直接拋出異常僵蛛。
·CallerRunsPolicy:只用調(diào)用者所在線程來運行任務。
·DiscardOldestPolicy:丟棄隊列里最近的一個任務迎变,并執(zhí)行當前任務充尉。
·DiscardPolicy:不處理,丟棄掉衣形。
6 keepAliveTime 線程活動保持時間
7 TimeUnit 線程活動保持時間的單位
向線程池提交任務
可以使用兩個方法向線程池提交任務驼侠,分別為execute()和submit()方法。
- execute()方法用于提交不需要返回值的任務
- submit()方法用于提交需要返回值的任務谆吴。線程池會返回一個future類型的對象倒源。
通過調(diào)用線程池的shutdown或shutdownNow方法來關(guān)閉線程池。
線程池的監(jiān)控:可以使用以下屬性
taskCount:線程池需要執(zhí)行的任務數(shù)量句狼。
completedTaskCount:線程池在運行過程中已完成的任務數(shù)量相速,小于或等于taskCount。
largestPoolSize:線程池里曾經(jīng)創(chuàng)建過的最大線程數(shù)量鲜锚。通過這個數(shù)據(jù)可以知道線程池是否曾經(jīng)滿過突诬。如該數(shù)值等于線程池的最大大小,則表示線程池曾經(jīng)滿過芜繁。
getPoolSize:線程池的線程數(shù)量旺隙。如果線程池不銷毀的話,線程池里的線程不會自動銷毀骏令,所以這個大小只增不減蔬捷。
getActiveCount:獲取活動的線程數(shù)。
第10章 Executor框架
Executor框架的兩級調(diào)度模型
Executor框架主要由3大部分組成如下榔袋。
任務:包括被執(zhí)行任務需要實現(xiàn)的接口:Runnable接口或Callable接口周拐。
任務的執(zhí)行:包括任務執(zhí)行機制的核心接口Executor,以及繼承自Executor的ExecutorService接口凰兑。Executor框架有兩個關(guān)鍵類實現(xiàn)了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)妥粟。
-
異步計算的結(jié)果。包括接口Future和實現(xiàn)Future接口的FutureTask類
Executor是一個接口吏够,它是Executor框架的基礎勾给,它將任務的提交與任務的執(zhí)行分離開 來。 ThreadPoolExecutor是線程池的核心實現(xiàn)類锅知,用來執(zhí)行被提交的任務播急。 ScheduledThreadPoolExecutor是一個實現(xiàn)類,可以在給定的延遲后運行命令售睹,或者定期執(zhí)行命令桩警。 Future接口和實現(xiàn)Future接口的FutureTask類,代表異步計算的結(jié)果昌妹。 Runnable接口和Callable接口的實現(xiàn)類捶枢,都可以被ThreadPoolExecutor或Scheduled-ThreadPoolExecutor執(zhí)行握截。
Executor框架的使用示意圖
Executor框架的成員
Executor框架的主要成員:ThreadPoolExecutor、ScheduledThreadPoolExecutor柱蟀、Future接口川蒙、Runnable接口、Callable接口和Executors长已。
ThreadPoolExecutor:
Executors可以創(chuàng)建3種類型的ThreadPoolExecutor:SingleThreadExecutor畜眨、FixedThreadPool和CachedThreadPool。
ScheduledThreadPoolExecutor:
Executors可以創(chuàng)建2種類型的ScheduledThreadPoolExecutor:
- ScheduledThreadPoolExecutor术瓮。包含若干個線程的ScheduledThreadPoolExecutor康聂。
- SingleThreadScheduledExecutor。只包含一個線程的ScheduledThreadPoolExecutor胞四。
Future接口
Future接口和實現(xiàn)Future接口的FutureTask類用來表示異步計算的結(jié)果恬汁。當我們把Runnable接口或Callable接口的實現(xiàn)類提交(submit)給ThreadPoolExecutor或ScheduledThreadPoolExecutor時,ThreadPoolExecutor或ScheduledThreadPoolExecutor會向我們返回一個FutureTask對象
可以執(zhí)行FutureTask.get()方法來等待任務執(zhí)行完成辜伟。當任務成功完成后FutureTask.get()將返回該任務的結(jié)果氓侧。
Runnable接口和Callable接口
Runnable接口和Callable接口的實現(xiàn)類,都可以被ThreadPoolExecutor或Scheduled-ThreadPoolExecutor執(zhí)行导狡。它們之間的區(qū)別是Runnable不會返回結(jié)果约巷,而Callable可以返回結(jié)果。
除了可以自己創(chuàng)建實現(xiàn)Callable接口的對象外旱捧,還可以使用工廠類Executors來把一個Runnable包裝成一個Callable独郎。
ScheduledThreadPoolExecutor詳解
ScheduledThreadPoolExecutor繼承自ThreadPoolExecutor。它主要用來在給定的延遲之后運
行任務枚赡,或者定期執(zhí)行任務氓癌。
FutureTask除了實現(xiàn)Future接口外,還實現(xiàn)了Runnable接口贫橙。因此贪婉,F(xiàn)utureTask可以交給Executor執(zhí)行,也可以由調(diào)用線程直接執(zhí)行(FutureTask.run())料皇。根據(jù)FutureTask.run()方法被執(zhí)行的時機谓松,F(xiàn)utureTask可以處于下面3種狀態(tài)。
- 未啟動践剂。FutureTask.run()方法還沒有被執(zhí)行之前,F(xiàn)utureTask處于未啟動狀態(tài)娜膘。當創(chuàng)建一個FutureTask逊脯,且沒有執(zhí)行FutureTask.run()方法之前,這個FutureTask處于未啟動狀態(tài)竣贪。
- 已啟動军洼。FutureTask.run()方法被執(zhí)行的過程中巩螃,F(xiàn)utureTask處于已啟動狀態(tài)。
- 已完成匕争。FutureTask.run()方法執(zhí)行完后正常結(jié)束避乏,或被取消(FutureTask.cancel(…)),或執(zhí)行FutureTask.run()方法時拋出異常而異常結(jié)束甘桑,F(xiàn)utureTask處于已完成狀態(tài)拍皮。