Java并發(fā)編程的藝術(shù)

第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)原子操作的三大問題

    1. ABA問題
    1. 循環(huán)時間長開銷大
    1. 只能保證一個共享變量的原子操作

第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()方法。

  1. execute()方法用于提交不需要返回值的任務
  2. 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大部分組成如下榔袋。

  1. 任務:包括被執(zhí)行任務需要實現(xiàn)的接口:Runnable接口或Callable接口周拐。

  2. 任務的執(zhí)行:包括任務執(zhí)行機制的核心接口Executor,以及繼承自Executor的ExecutorService接口凰兑。Executor框架有兩個關(guān)鍵類實現(xiàn)了ExecutorService接口(ThreadPoolExecutor和ScheduledThreadPoolExecutor)妥粟。

  3. 異步計算的結(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:

  1. ScheduledThreadPoolExecutor术瓮。包含若干個線程的ScheduledThreadPoolExecutor康聂。
  2. 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)。
  1. 未啟動践剂。FutureTask.run()方法還沒有被執(zhí)行之前,F(xiàn)utureTask處于未啟動狀態(tài)娜膘。當創(chuàng)建一個FutureTask逊脯,且沒有執(zhí)行FutureTask.run()方法之前,這個FutureTask處于未啟動狀態(tài)竣贪。
  2. 已啟動军洼。FutureTask.run()方法被執(zhí)行的過程中巩螃,F(xiàn)utureTask處于已啟動狀態(tài)。
  3. 已完成匕争。FutureTask.run()方法執(zhí)行完后正常結(jié)束避乏,或被取消(FutureTask.cancel(…)),或執(zhí)行FutureTask.run()方法時拋出異常而異常結(jié)束甘桑,F(xiàn)utureTask處于已完成狀態(tài)拍皮。
FutureTask的get和cancel的執(zhí)行示意圖
FutureTask的設計示意圖
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市跑杭,隨后出現(xiàn)的幾起案子铆帽,更是在濱河造成了極大的恐慌,老刑警劉巖德谅,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爹橱,死亡現(xiàn)場離奇詭異,居然都是意外死亡窄做,警方通過查閱死者的電腦和手機愧驱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椭盏,“玉大人组砚,你說我怎么就攤上這事∮购梗” “怎么了惫确?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蚯舱。 經(jīng)常有香客問我改化,道長,這世上最難降的妖魔是什么枉昏? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任陈肛,我火速辦了婚禮,結(jié)果婚禮上兄裂,老公的妹妹穿的比我還像新娘句旱。我一直安慰自己,他們只是感情好晰奖,可當我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布谈撒。 她就那樣靜靜地躺著,像睡著了一般匾南。 火紅的嫁衣襯著肌膚如雪啃匿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天,我揣著相機與錄音溯乒,去河邊找鬼夹厌。 笑死,一個胖子當著我的面吹牛裆悄,可吹牛的內(nèi)容都是我干的矛纹。 我是一名探鬼主播,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼光稼,長吁一口氣:“原來是場噩夢啊……” “哼或南!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起钟哥,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤迎献,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后腻贰,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吁恍,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年播演,在試婚紗的時候發(fā)現(xiàn)自己被綠了冀瓦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡写烤,死狀恐怖翼闽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情洲炊,我是刑警寧澤感局,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站暂衡,受9級特大地震影響询微,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狂巢,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一撑毛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧唧领,春花似錦藻雌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至受啥,卻和暖如春歉摧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腔呜。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工叁温, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人核畴。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓膝但,卻偏偏與公主長得像,于是被迫代替她去往敵國和親谤草。 傳聞我的和親對象是個殘疾皇子跟束,可洞房花燭夜當晚...
    茶點故事閱讀 45,870評論 2 361

推薦閱讀更多精彩內(nèi)容