《Java併發編程的藝術》筆記

《Java併發編程的藝術》筆記

第一章 併發編程的挑戰

第二章 Java併發機制的底層實現原理

volatile的兩條實現原則:java

  1. Lock前綴指令會引發處理器緩存回寫到內存
  2. 一個處理器的緩存回寫到內存會致使其餘處理器的緩存無效。

volatile的使用優化:共享變量會被頻繁讀寫時,能夠經過追加爲64字節以提升併發編程的效率。由於目前主流處理器高速緩存行是64個字節寬,不支持部分填充緩存行,經過追加到64字節的方式填滿高速緩衝區的緩存行,避免各元素加載到同一緩存行而互相鎖定。(Java7後可能不生效,由於Java7更智能,會淘汰或從新排列無用字段,須要使用其餘追加字節的方式算法

Java對象頭編程

長度 內容 說明
32/64bit Mark Word 存儲對象的hashCode或鎖信息等
32/64bit Class Metadata Address 存儲到對象類型數據的指針
32/64bit Array Length 數組的長度(僅噹噹前對象爲數組時存在)

CAS操做,即Compare And Swap,比較並交換。CAS操做須要輸入兩個數值,一箇舊值(指望操做前的值)和一個新值,在操做期間先比較舊值有沒有發生變化,若是沒有則交換成新值,不然不交換。數組

鎖有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態。這幾種狀態會隨着競爭狀況逐漸升級,只升不降。緩存

偏向鎖安全

  • 當一個線程訪問同步塊並獲取鎖時,會在對象頭和棧幀中的鎖記錄裏存儲鎖偏向的線程ID,之後該線程再次加鎖解鎖時不須要進行CAS操做,只需簡單地測試一下對象頭的Mark Word裏是否存儲着指向當前線程的偏向鎖。
  • 偏向鎖使用了一種等到競爭出現才釋放鎖的機制,因此當其餘線程嘗試競爭偏向鎖時,持有偏向鎖的線程纔會釋放鎖。
  • 偏向鎖的撤銷,須要等待全局安全點(在這個時間點上沒有正在執行的字節碼)。它會首先暫停擁有偏向鎖的線程,而後檢查持有偏向鎖的線程是否活着,若是線程不處於活動狀態,則將對象頭設置成無鎖狀態;若是線程仍然活着,擁有偏向鎖的棧會被執行,遍歷偏向對象的鎖記錄,棧中的鎖記錄和對象頭的Mark Word要麼從新偏向於其餘線程,要麼恢復到無鎖或者標記對象不適合做爲偏向鎖,最後喚醒暫停的線程。
  • 偏向鎖默認啓動,可是它在應用程序啓動幾秒鐘以後才激活,若有必要可使用JVM參數來關閉延遲:-XX:BiasedLockingStartupDelay=0。若是肯定應用程序裏全部的鎖一般狀況下處於競爭狀態,能夠經過JVM參數來關閉偏向鎖:-XX:-UseBiasedLocking=false,那麼程序默認會進入輕量級鎖狀態。

輕量級鎖併發

  • 線程在執行同步塊以前,JVM會先在當前線程的棧幀中建立用於存儲鎖記錄的空間,並將對象頭中的Mark Word複製到鎖記錄中(Displaced Mark Word)。而後線程嘗試經過CAS將對象頭中的Mark Word替換爲指向鎖記錄的指針。若是成功則當前線程得到鎖,不然說明其餘線程競爭鎖,當前線程嘗試使用自旋來獲取鎖。
  • 輕量級鎖解鎖時,會使用原子的CAS操做將Displaced Mark Word替換回到對象頭,若是成功則表示沒有競爭發生,不然表示當前鎖存在競爭,鎖會升級爲重量級鎖。

重量級鎖框架

  • 其餘線程試圖獲取鎖時都被阻塞,持有鎖的線程釋放鎖以後喚醒這些線程,被喚醒的線程再搶。

鎖的優缺點對比函數

優勢 缺點 適用場景
偏向鎖 加鎖和解鎖無需額外的消耗,和執行非同步方法相比僅存在納秒級的差距 若是線程間存在鎖競爭,會帶來額外的鎖撤銷的消耗 適用於只有一個線程訪問同步塊場景
輕量級鎖 競爭的線程不會阻塞,提升了程序的響應速度 若是始終得不到鎖競爭的線程,使用自旋會消耗CPU 追求響應時間,同步塊執行速度很是快
重量級鎖 線程競爭不適用自旋,不會消耗CPU 線程阻塞,響應時間緩慢 追求吞吐量,同步塊執行速度較慢

CAS實現原子操做的三大問題高併發

  1. ABA問題。一個值從A變成B又變成A,使用CAS檢查時覺得沒有變化,但實際上卻變化了。解決思路是使用版本號。
  2. 循環時長長,開銷大。
  3. 只能保證一個共享變量的原子操做。(Java1.5之後,JDK提供了AtomicReference類來保證引用對象之間的原子性,能夠把多個變量放在一個對象裏來進行CAS操做)

第三章 Java內存模型

  • 併發編程模型的兩個關鍵問題:線程之間如何通訊,如何同步
  • 在命令式編程中,線程之間的通訊機制有兩種:共享內存和消息傳遞。

內存屏障類型表

屏障類型 說明
LoadLoad Barriers 確保Load1數據的裝載先於Load2及全部後序裝置指令的裝載
StoreStore Barriers 確保Store1數據對其餘處理器可見(刷新到內存)先於Store2及全部後序存儲指令的存儲
LoadStore Barriers 確保Load1數據裝載先於Store2及全部後序的存儲指令刷新到內存
StoreLoad Barriers 確保Store1數據對其餘處理器變得可見(指刷新到內存)先於Load2及全部後序裝載指令的裝載。StoreLoad Barriers會使改屏障以前的全部內存訪問指令(存儲和裝載指令)完成以後,才執行該屏障以後的內存訪問指令
  • 編譯器、runtime和處理器都必須遵照as-if-serial語義,即無論怎麼重排序,(單線程)程序的執行結果不能被改變。

volatile的內存語義

volatile變量自身具備如下特性:

  • 可見性:對一個volatile變量的讀,老是能看到(任意線程)對這個volatile變量最後的寫入。
  • 原子性:對任意單個volatile變量的讀/寫具備原子性,但相似於volatile++這種複合操做不具備原子性。

volatile寫-讀的內存語義:

  • 當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值刷新到主內存。
  • 當讀一個volatile變量時,JMM會把該線程對應的本地內存置爲無效。線程接下來將從主內存中讀取共享變量。

JMM內存屏障插入策略:

  • 在每一個volatile寫操做先後分別插入StoreStore、StoreLoad屏障。
  • 在每一個volatile讀操做後面插入LoadLoad、LoadStore屏障。

鎖的內存語義

  • 當線程獲取鎖時,JMM會把該線程對應的本地內存置爲無效。
  • 當線程釋放鎖時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存中。
  • 公平鎖和非公平鎖的內存語義:
    • 公平鎖和非公平鎖釋放時,最後都要寫一個volatile變量state。
    • 公平鎖獲取時,首先會去讀volatile變量。
    • 非公平鎖獲取時,首先會用CAS操做更新volatile變量,這個操做同時具備volatile讀/寫的內存語義。

final域的內存語義

對於final域,編譯器和處理器要遵循兩個重排序規則:

  1. 在構造函數內對一個final域的寫入,與隨後把這個被構造對象的引用賦值給一個引用變量,這兩個操做之間不能重排序。
  2. 初次讀一個包含final域的對象的應用,與隨後初次讀這個final域,這兩個操做之間不能重排序。
  • 讀final域的重排序規則能夠確保:在讀一個對象的final域以前,必定會先讀包含這個final域的對象的引用。
  • 寫final域的重排序規則能夠確保:在對象引用爲任意線程可見以前,對象的final域已經被正確初始化。(前提是對象引用不能在構造函數中逸出

第四章 Java併發編程基礎

  • 線程的優先級僅僅是一部分決定因素,由於線程的切換具備隨機性,並且針對不一樣的系統而言,優先級這個概念可能就不存在,其僅僅是決定程序設計的衡量的一個標準。

等待/通知的經典範式

  • 等待方遵循以下原則:
    1. 獲取對象的鎖
    2. 若是條件不知足,那麼調用對象的wait()方法,被通知後仍要檢查條件。
    3. 條件知足則執行對應的邏輯。
  • 通知方遵循以下原則:
    1. 得到對象的鎖。
    2. 改變條件。
    3. 通知全部等待在對象上的線程。

第五章 Java中的鎖

Lock接口提供的synchronized關鍵字不具有的主要特性

特性 描述
嘗試非阻塞地獲取鎖 當前線程嘗試獲取鎖,若是這一時刻鎖沒有被其餘線程獲取到,則成功獲取並持有鎖
能被中斷地獲取鎖 獲取到鎖的線程可以相應中斷,當獲取到鎖的線程被中斷時,中斷異常將會被拋出,同時鎖會被釋放
超時獲取鎖 在指定的截止時間以前獲取鎖,若是截止時間到了仍舊沒法獲取鎖,則返回

隊列同步器(AbstractQueuedSynchronizer)

同步器提供以下3個方法來訪問或修改同步狀態(int成員變量):

  1. getState():獲取當前同步狀態
  2. setState(int newState):設置當前同步狀態
  3. compareAndSetState(int expect, int update):使用CAS設置當前狀態,該方法可以保證狀態設置的原子性。

同步器可重寫的方法

方法名稱 描述
protected boolean tryAcquire(int arg) 獨佔式獲取同步狀態,實現該方法須要查詢當前狀態並判斷同步狀態是否符合預期,而後再進行CAS設置同步狀態
protected boolean tryRelease(int arg) 獨佔式釋放同步狀態,等待獲取同步狀態的線程將有機會獲取同步狀態
protected int tryAcquireShared(int arg) 共享式獲取同步狀態,返回大於等於0的值,表示獲取成功,反之失敗
protected boolean tryReleaseShared(int arg) 共享式釋放同步狀態
protected boolean isHeldExclusively() 當前同步器是否在獨佔模式下被線程佔用,通常該方法表示是否被當前線程所獨佔

同步器提供的模板方法

同步隊列及等待隊列的節點屬性類型與名稱以及描述

  • 同步隊列中的結點只有當其前驅結點爲頭結點時,才嘗試獲取同步狀態。
  • 讀寫鎖的同步狀態高16位表示讀狀態、低16位表示寫狀態。

LockSupport

  • 當須要阻塞或喚醒一個線程的時候,都會使用LockSupport工具類來完成相應工做。LockSupport定義了一組公共靜態方法,這些方法提供了最基本的線程阻塞和喚醒功能,而LockSupport也成爲了構建同步組建的基礎工具。
  • LockSupport提供的阻塞和喚醒方法(其中參數blocker是用來標識當前線程在等待的對象,便於問題排查和系統監控)
方法名稱 描述
void park(Object blocker) 阻塞當前線程,若是調用unpark方法或者當前線程被終端,才能從park方法返回
void parkNanos(Object blocker, long nanos) 阻塞當前線程,最長不超過nanos納秒,返回條件在park的基礎上增長了超時返回
void parkUntil(Object blocker, long deadline) 阻塞當前線程,直到deadline時間
void unpark(Thread thread) 喚醒處於阻塞狀態的線程thread

第六章 Java併發容器和框架

  • ConcurrentHashMap使用鎖分段技術,將數據分紅一段一段地存儲,而後給每一段數據配一把鎖,當一個線程佔用鎖訪問其中一個段數據的時候,其餘段的數據也能被其餘線程訪問。
  • ConcurrentLinkedQueue非阻塞的線程安全隊列

阻塞隊列

  • 阻塞隊列(BlockingQueue)是一個支持兩個附加操做的隊列。這兩個附加的操做支持阻塞的插入和移除方法。
    1. 支持阻塞的插入方法:當隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿。
    2. 支持阻塞的移除方法:當隊列空時,獲取元素的線程會等待隊列變爲非空。
  • 插入和移除操做的4種處理方式
方法/處理方式 拋出異常 返回特殊值 一直阻塞 超時退出
插入方法 add(e) offer(e) put(e) offer(e, time, unit)
移除方法 remove() poll() take() poll(time, unit)
檢查方法 element() peek() 不可用 不可用
  • 拋出異常:當隊列滿時,若是再往隊列裏插入元素會拋出IllegalStateException("Queue full")異常。當隊列空時,從隊列裏獲取元素會拋出NoSuchElementException異常。
  • 返回特殊值:當往隊列插入元素時,會返回元素是否插入成功,成功返回true。若是是移除方法,則從隊列裏取出一個元素,若是沒有則返回null。
  • 一直阻塞:當阻塞隊列滿時,若是生產者線程往隊列裏put元素,隊列會一直阻塞生產者線程,直到隊列可用或者響應中斷退出。當隊列空時,若是消費者線程從隊列列take元素,隊列會阻塞消費者線程,直到隊列不爲空。
  • 超時退出:當阻塞隊列滿時,若是生產者線程往隊列裏插入元素,隊列會阻塞生產者線程一段時間,若是超時則退出。

JDK7提供了7個阻塞隊列:

  1. ArrayBlockingQueue:用數組實現的有界阻塞隊列,按FIFO原則對元素進行排序。
  2. LinkedBlockingQueue:用鏈表實現的有界阻塞隊列,默認和最大長度爲Integer.MAX_VALUE,按FIFO原則對元素進行排序。
  3. PriorityBlockingQueue:支持優先級的無界阻塞隊列,默認狀況下元素採起天然順序升序排列。不保證同優先級元素的順序。
  4. DelayQueue:支持延時獲取元素的無界阻塞隊列。隊列使用PriorityQueue實現,隊列中的元素必須實現Delayed接口,在建立元素時能夠指定多久才能從隊列中獲取當前元素。只有在延遲期滿時才能從隊列中提取元素。可應用於:
    • 緩存系統的設計:用DelayQueue保存緩存元素的有效期,使用一個線程循環查詢DelayQueue,一旦能從DelayQueue中獲取元素表示緩存有效期到了。
    • 定時任務調度:使用DelayQueue保存當天將會執行的任務和執行時間,一旦從DelayQueue中獲取到任務就開始執行,好比TimerQueue就是使用DelayQueue實現的。
  5. SynchronousQueue:不存儲元素的阻塞隊列。每個put操做必須等待一個take操做,不然不能繼續添加元素。
  6. LinkedTransferQueue:鏈表結構組成的無界阻塞TransferQueue隊列。相對於其餘阻塞隊列,LinkedTransferQueue多了tryTransfer和transfer方法。
    • transfer方法:若是當前有消費者正在等待接收元素,transfer方法能夠把生產者傳入的元素馬上傳給消費者。若是沒有消費者在等待接收元素,則將元素存放在隊列tail節點並等到鈣元素被消費者消費了才返回。
    • tryTransfer方法:若是沒有消費者等待接收元素,則當即返回false。
  7. LinkedBlockingDeque:鏈表結構組成的雙向阻塞隊列。

Fork/Join框架

  • Fork/Join框架是一個用於並行執行任務的框架,是一個把大任務分隔成若干個小任務,最終彙總每一個小任務結果後獲得大任務結果的框架。
  • 工做竊取算法:假如咱們須要作一個比較大的任務,咱們能夠把這個任務分割爲若干互不依賴的子任務,爲了減小線程間的競爭,因而把這些子任務分別放到不一樣的隊列裏,併爲每一個隊列建立一個單獨的線程來執行隊列裏的任務,線程和隊列一一對應,好比A線程負責處理A隊列裏的任務。可是有的線程會先把本身隊列裏的任務幹完,而其餘線程對應的隊列裏還有任務等待處理。幹完活的線程與其等着,不如去幫其餘線程幹活,因而它就去其餘線程的隊列裏竊取一個任務來執行。而在這時它們會訪問同一個隊列,因此爲了減小竊取任務線程和被竊取任務線程之間的競爭,一般會使用雙端隊列,被竊取任務線程永遠從雙端隊列的頭部拿任務執行,而竊取任務的線程永遠從雙端隊列的尾部拿任務執行。工做竊取算法的優勢是充分利用線程進行並行計算,並減小了線程間的競爭,其缺點是在某些狀況下仍是存在競爭,好比雙端隊列裏只有一個任務時。而且消耗了更多的系統資源,好比建立多個線程和多個雙端隊列。
  • ForkJoinTask:咱們要使用ForkJoin框架,必須首先建立一個ForkJoin任務。它提供在任務中執行fork()和join()操做的機制,一般狀況下咱們不須要直接繼承ForkJoinTask類,而只須要繼承它的子類,Fork/Join框架提供瞭如下兩個子類:
    • RecursiveAction:用於沒有返回結果的任務。
    • RecursiveTask :用於有返回結果的任務。
  • ForkJoinPool :ForkJoinTask須要經過ForkJoinPool來執行,任務分割出的子任務會添加到當前工做線程所維護的雙端隊列中,進入隊列的頭部。當一個工做線程的隊列裏暫時沒有任務時,它會隨機從其餘工做線程的隊列的尾部獲取一個任務。
  • ForkJoinTask在執行的時候可能會拋出異常,可是咱們沒辦法在主線程裏直接捕獲異常,因此ForkJoinTask提供了isCompletedAbnormally()方法來檢查任務是否一件拋出異常或已經被取消了,而且能夠經過ForkJoinTask的getException方法獲取異常。其中,getException方法返回Throwable對象,若是任務被取消了則返回CancellationException,若是任務沒有完成或者沒有拋出異常則返回null。

第七章 Java中的13個原子操做類(Ps:認真數了一下發現只有12個)

  • 原子更新方式
    • 原子更新基本類型
    • 原子更新數組
    • 原子更新引用
    • 原子更新屬性(字段)
  • 原子更新基本類型
    • AtomicBoolean :原子更新布爾類型
    • AtomicInteger: 原子更新整型
    • AtomicLong: 原子更新長整型
  • 原子更新數組
    • AtomicIntegerArray :原子更新整型數組裏的元素
    • AtomicLongArray :原子更新長整型數組裏的元素
    • AtomicReferenceArray : 原子更新引用類型數組的元素
  • 原子更新引用類型
    • AtomicReference :原子更新引用類型
    • AtomicReferenceFieldUpdater :原子更新引用類型裏的字段
    • AtomicMarkableReference:原子更新帶有標記位的引用類型。能夠原子更新一個布爾類型的標記位和應用類型
  • 原子更新字段類
    • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
    • AtomicLongFieldUpdater:原子更新長整型字段的更新器
    • AtomicStampedReference:原子更新帶有版本號的引用類型。該類將整型數值與引用關聯起來,可用於原子的更新數據和數據的版本號,能夠解決使用CAS進行原子更新時可能出現的ABA問題。

第八章 Java中的併發工具類

CountDownLatch

  • CountDownLatch容許一個或多個線程等待其餘線程完成操做。
  • CountDownLatch的構造函數接收一個int類型的參數做爲計數器,若是你想等待N個點完成,就傳入N。
  • 每次調用CountDownLatch的countDown方法時,N就減1,CountDownLatch的await方法會阻塞當前線程,直到N變成0。

CyclicBarrier

  • CyclicBarrier的做用是讓一組線程到達一個屏障(也能夠稱之爲同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,全部被屏障攔截的線程才能繼續運行。
  • CyclicBarrier(int parties)構造函數接收一個int參數用來設置攔截線程的數量,還有一個更高級的構造函數CyclicBarrier(int parties, Runnable barrierAction)設定第一個到達屏障的線程執行barrierAction。
  • getNumberWaiting方法能夠得到CyclicBarrier阻塞的線程數量,isBroken()方法用來了解阻塞的線程是否被中斷。

Semaphore

  • Semaphore(信號量)用來控制同時訪問特定資源的線程數量,經過協調各個線程以保證合理的使用公共資源。
  • Semaphore(int permits)構造方法接收一個int參數,表示可用的許可證數量。
  • 每次線程使用Semaphore的acquire()方法獲取一個許可證,用完後調用release()方法歸還。
  • 其餘方法
    • intavailablePermits():返回此信號量中當前可用的許可證數。
    • intgetQueueLength():返回正在等待獲取許可證的線程數。
    • booleanhasQueuedThreads():是否有線程正在等待獲取許可證。
    • void reducePermits(int reduction):減小reduction個許可證,是個protected方法。
    • Collection getQueuedThreads():返回全部等待獲取許可證的線程集合,是個protected方法。

Exchanger

  • 用於線程間交換數據,每兩次執行Exchanger的exchange(V data)方法,則交換兩次執行的數據並返回。可用於校對工做。

第九章 Java中的線程池

線程池的3個好處:

  1. 下降資源消耗:經過重複利用已建立的線程下降線程建立和銷燬形成的消耗。
  2. 提升響應速度:當任務到達時,任務能夠不須要等到線程建立就能當即執行。
  3. 提升線程的可管理性:線程是稀缺資源,若是無限制地建立,不只會消耗系統資源,還會下降系統的穩定性。使用線程池能夠進行統一分配、調優和監控。

當提交一個新任務到線程池時,線程池的流程:

  1. 若是當前運行的線程少於corePoolSize,則建立新線程來執行任務。
  2. 若是運行的線程等於或多於corePollSize,則將任務加入BlockingQueue。
  3. 若是沒法將任務加入BlockingQueue(隊列已滿),則建立新的線程來處理任務。
  4. 若是建立新線程將致使當前運行的線程數超過maximumPoolSize,任務將被拒絕,並調用RejectedExecutionHandler.rejectedExecution()方法。

線程池的構造方法

ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds, runnableTaskQueue, handler),其中:

  1. corePoolSize(線程池的基本大小):當提交一個任務到線程池時,線程池會建立一個線程來執行任務,即便其餘空閒的基本線程可以執行新任務也會建立線程,直到須要執行的任務數大於線程池基本大小。若是調用了線程池的prestartAllCoreThreads()方法,線程池會提早建立並啓動全部基本線程。
  2. runnableTaskQueue(任務隊列):用於保存等待執行的任務的阻塞隊列。能夠選擇ArratBlockingQueue, LinkedBlockingQueue, SynchronousQueue, PriorityBlockingQueue。
  3. maximumPoolSize(線程池最大數量):線程池容許建立的最大線程數。
  4. ThreadFactory:用於設置建立線程的工廠,能夠經過線程工廠給每一個建立出來的線程設置更有意義的名字。
  5. RejectedExecutionHandler(飽和策略):當隊列和線程池都滿了,說明線程池處於飽和狀態,那麼必須採起一種策略處理提交的新任務。這個策略默認狀況下是AbortPolicy,表示沒法處理新任務時拋出異常。策略有下列幾種:
    • AbortPolicy:直接拋出異常
    • CallerRunsPolicy:只用調用者所在線程來運行任務。
    • DiscardOldestPolicy:丟棄隊列裏最近的一個任務,並執行當前任務。
    • DiscardPolicy:不處理,丟棄掉。
    • 其餘應用場景須要實現RejectedExecutionHandler接口自定義。
  6. keepAliveTime(線程活動保持時間):線程池的工做線程空閒後,保持存活的時間。若是任務多且執行時間短,能夠調高存活時間提升線程利用率。
  7. TimeUnit(線程活動保持時間的單位)

向線程池提交任務有兩種方式:

  1. execute(Runnable command):沒有返回值
  2. submit(Callable task):返回一個future類型的對象,經過這個對象判斷任務是否執行成功,並經過其get()方法來獲取返回值,該方法會阻塞當前線程直到任務完成。

線程池的關閉有兩種方法:

  1. shutdown():將線程池的狀態設置成SHUTDOWN狀態,而後中斷全部沒有正在執行任務的線程。
  2. shutdownNow():將線程池的狀態設置成STOP,而後嘗試中止全部的正在執行或暫停任務的線程,並返回等待執行任務的列表。

只要調用了兩種關閉方法的任一種,isShutdown()方法都會返回true。當且僅當全部任務都關閉,CIA表示線程池關閉成功,這是isTerminaed()方法纔會返回true。

合理配置線程池(設N爲CPU個數)

  • CPU密集型任務,應配置儘量少的線程,如N+1。
  • IO密集型任務,應配置儘量多的線程,如2N。
  • 優先級不一樣的任務能夠考慮使用優先級隊列priorityBlockingQueue來處理,但優先級低的任務可能永遠不被執行。
  • 使用有界隊列能增長系統的穩定性和預警性,避免隊列愈來愈多撐滿內存,致使系統不可用。

線程池的監控

監控線程池的時候可使用如下屬性:

  • taskCount:線程池須要執行的任務數量。
  • completedTaskCount:線程池在運行過程當中已完成的任務數量,小於或等於taskCount。
  • largestPoolSize:線程池裏曾經建立過的最大線程數量。經過這個數據能夠知道線程池是否曾經滿過。
  • getPoolSize:線程池的線程數量。若是線程池不銷燬的話,線程池裏的線程不會自動銷燬,因此這個大小隻增不減。
  • getActiveCount:獲取活動的線程數。

能夠經過繼承線程池來自定義線程池,重寫線程池的beforeExecute, afterExecute和terminated方法,也能夠在任務執行先後和線程池關閉前執行一些代碼來進行監控。例如,監控任務的平均執行時間、最大執行時間和最小執行時間等。這幾個方法在線程池裏都是空方法。

第十章 Executor框架

工廠類Executors可建立3種類型的ThreadPoolExecutor:

  • FixedThreadPool:可重用固定線程數的線程池。new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
  • SingleThreadExecutor:使用單個worker線程的Executor。new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())
  • CachedThreadPool:會根據須要建立新線程的線程池。new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnbalbe>())

工廠類Executors可建立2種類型的ScheduledThreadPoolExecutor:

  • ScheduledThreadPoolExecutor:包含若干個線程的ScheduledThreadPoolExecutor。
  • SingleThreadScheduledExecutor:只包含一個線程的ScheduledThreadPoolExecutor。

第十一章 Java併發編程實踐

相關文章
相關標籤/搜索