1. 程式人生 > 其它 >Java併發程式設計系列之三JUC概述

Java併發程式設計系列之三JUC概述

上篇文章為解決多執行緒中出現的同步問題引入了鎖的概念,上篇文章介紹的是Synchronized關鍵字鎖,本篇文章介紹更加輕量級的鎖Lock介面及引出JUC的相關知識。

本文不力爭闡釋清楚JUC框架的所有內容,而是站在一定的高度下,瞭解Juc下包的設計與實現。

目錄

一、LOCK鎖概述

實現同步的另外一種方式是Lock鎖。

Lock鎖是一個介面,其所有的實現類為:

  • ReentrantLock(可重入鎖)
  • ReentrantReadWriteLock.ReadLock(可重入讀寫鎖中的讀鎖)
  • ReentrantReadWriteLock.WriteLock(可重入讀寫鎖中的寫鎖)

與synchronized不同的是,使用LOCK鎖與其有六個區別

1 synchronized 是Java內建關鍵字,Lock 是一個介面
2 synchronized 無法判斷是否獲取鎖,Lock 可以判斷是否獲取鎖
3 synchronized 可以自動釋放鎖,Lock 必須手動釋放鎖,如果不釋放就會造成死鎖
4 同一個鎖物件,執行緒A synchronized獲取之後,執行緒B只能等待,造成阻塞,Lock 並不會等待
5 synchronized 可重入鎖,不可中斷,非公平鎖,Lock 可重入鎖,可以判斷,非公平鎖(可設定)
6 synchronized 適合少量同步程式碼,Lock 適合大量同步程式碼

Lock介面位於 java.util.concurrent.locks 包下,在父級的包下有兩個包。

  • java.util.concurrent:包下主要是包括併發相關的介面與類,阻塞佇列、執行緒池等,裡面包含 59 個類或者介面
  • java.util.concurrent.atomic: 該包下主要包括原子性操作相關的類,比如常用的AtomicInteger、AtomicBoolean、AtomicIntegerArry等,裡面包含18個類或者介面

其中父級的包java.util .concurrent涉及到Java多執行緒最重要的一部分——JUC程式設計。

二、JUC概述

JUC就是java.util .concurrent工具包的簡稱。這是一個處理執行緒的工具包,JDK 1.5開始出現的,在此包中增加了在併發程式設計中很常用的工具類。

用於定義類似於執行緒的自定義子系統,包括執行緒池,非同步 IO 和輕量任務框架;

還提供了設計用於多執行緒上下文中的 Collection 實現等;

下圖為JUC涉及到的所有知識點。

JUC應該包括五個部分的內容。

1、Lock框架

① 介面: Condition

Condition為介面型別,它將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的物件,以便通過將這些物件與任意 Lock 實現組合使用,為每個物件提供多個等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。可以通過await(),signal()來休眠/喚醒執行緒。

②介面: Lock

Lock為介面型別,Lock實現提供了比使用synchronized方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支援多個相關的Condition物件。

③介面: ReadWriteLock

ReadWriteLock為介面型別, 維護了一對相關的鎖,一個用於只讀操作,另一個用於寫入操作。只要沒有 writer,讀取鎖可以由多個 reader 執行緒同時保持。寫入鎖是獨佔的。

④抽象類: AbstractOwnableSynchonizer

AbstractOwnableSynchonizer為抽象類,可以由執行緒以獨佔方式擁有的同步器。此類為建立鎖和相關同步器(伴隨著所有權的概念)提供了基礎。AbstractOwnableSynchronizer 類本身不管理或使用此資訊。但是,子類和工具可以使用適當維護的值幫助控制和監視訪問以及提供診斷。

⑤抽象類(long): AbstractQueuedLongSynchronizer

AbstractQueuedLongSynchronizer為抽象類,以 long 形式維護同步狀態的一個 AbstractQueuedSynchronizer 版本。此類具有的結構、屬性和方法與 AbstractQueuedSynchronizer 完全相同,但所有與狀態相關的引數和結果都定義為 long 而不是 int。當建立需要 64 位狀態的多級別鎖和屏障等同步器時,此類很有用。

⑥ 核心抽象類(int): AbstractQueuedSynchonizer

AbstractQueuedSynchonizer為抽象類,其為實現依賴於先進先出 (FIFO) 等待佇列的阻塞鎖和相關同步器(訊號量、事件,等等)提供一個框架。此類的設計目標是成為依靠單個原子 int 值來表示狀態的大多數同步器的一個有用基礎。

⑦鎖常用類: LockSupport

LockSupport為常用類,用來建立鎖和其他同步類的基本執行緒阻塞原語。LockSupport的功能和"Thread中的 Thread.suspend()和Thread.resume()有點類似",LockSupport中的park() 和 unpark() 的作用分別是阻塞執行緒和解除阻塞執行緒。但是park()和unpark()不會遇到“Thread.suspend 和 Thread.resume所可能引發的死鎖”問題。

⑧鎖常用類: ReentrantLock

ReentrantLock為常用類,它是一個可重入的互斥鎖 Lock,它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行為和語義,但功能更強大。

⑨鎖常用類: ReentrantReadWriteLock

ReentrantReadWriteLock是讀寫鎖介面ReadWriteLock的實現類,它包括Lock子類ReadLock和WriteLock。ReadLock是共享鎖,WriteLock是獨佔鎖。

⑩鎖常用類: StampedLock

它是java8在java.util.concurrent.locks新增的一個API。StampedLock控制鎖有三種模式(寫,讀,樂觀讀),一個StampedLock狀態是由版本和模式兩個部分組成,鎖獲取方法返回一個數字作為票據stamp,它用相應的鎖狀態表示並控制訪問,數字0表示沒有寫鎖被授權訪問。在讀鎖上分為悲觀鎖和樂觀鎖。

2、Tools類

①工具常用類: CountDownLatch

CountDownLatch為常用類,它是一個同步輔助類,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。

②具常用類: CyclicBarrier

CyclicBarrier為常用類,其是一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的執行緒的程式中,這些執行緒必須不時地互相等待,此時 CyclicBarrier 很有用。因為該 barrier 在釋放等待執行緒後可以重用,所以稱它為迴圈 的 barrier。)

③工具常用類: Phaser

Phaser是JDK 7新增的一個同步輔助類,它可以實現CyclicBarrier和CountDownLatch類似的功能,而且它支援對任務的動態調整,並支援分層結構來達到更高的吞吐量。

④ 工具常用類: Semaphore

Semaphore為常用類,其是一個計數訊號量,從概念上講,訊號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire(),然後再獲取該許可。每個 release() 新增一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可物件,Semaphore 只對可用許可的號碼進行計數,並採取相應的行動。通常用於限制可以訪問某些資源(物理或邏輯的)的執行緒數目。

⑤工具常用類: Exchanger

Exchanger是用於執行緒協作的工具類, 主要用於兩個執行緒之間的資料交換。它提供一個同步點,在這個同步點,兩個執行緒可以交換彼此的資料。這兩個執行緒通過exchange()方法交換資料,當一個執行緒先執行exchange()方法後,它會一直等待第二個執行緒也執行exchange()方法,當這兩個執行緒到達同步點時,這兩個執行緒就可以交換資料了。

3、Collections: 併發集合

併發集合的類結構關係

①Queue: ArrayBlockingQueue

一個由陣列支援的有界阻塞佇列。此佇列按 FIFO(先進先出)原則對元素進行排序。佇列的頭部 是在佇列中存在時間最長的元素。佇列的尾部 是在佇列中存在時間最短的元素。新元素插入到佇列的尾部,佇列獲取操作則是從佇列頭部開始獲得元素。

②Queue: LinkedBlockingQueue

一個基於已連結節點的、範圍任意的 blocking queue。此佇列按 FIFO(先進先出)排序元素。佇列的頭部 是在佇列中時間最長的元素。佇列的尾部 是在佇列中時間最短的元素。新元素插入到佇列的尾部,並且佇列獲取操作會獲得位於佇列頭部的元素。連結佇列的吞吐量通常要高於基於陣列的佇列,但是在大多數併發應用程式中,其可預知的效能要低。

③Queue: LinkedBlockingDeque

一個基於已連結節點的、任選範圍的阻塞雙端佇列。

④Queue: ConcurrentLinkedQueue

一個基於連結節點的無界執行緒安全佇列。此佇列按照 FIFO(先進先出)原則對元素進行排序。佇列的頭部 是佇列中時間最長的元素。佇列的尾部 是佇列中時間最短的元素。新的元素插入到佇列的尾部,佇列獲取操作從佇列頭部獲得元素。當多個執行緒共享訪問一個公共 collection 時,ConcurrentLinkedQueue 是一個恰當的選擇。此佇列不允許使用 null 元素。

⑤ Queue: ConcurrentLinkedDeque

是雙向連結串列實現的無界佇列,該佇列同時支援FIFO和FILO兩種操作方式。

⑥ Queue: DelayQueue

延時無界阻塞佇列,使用Lock機制實現併發訪問。佇列裡只允許放可以“延期”的元素,佇列中的head是最先“到期”的元素。如果隊裡中沒有元素到“到期”,那麼就算佇列中有元素也不能獲取到。

⑦ Queue: PriorityBlockingQueue

無界優先順序阻塞佇列,使用Lock機制實現併發訪問。priorityQueue的執行緒安全版,不允許存放null值,依賴於comparable的排序,不允許存放不可比較的物件型別。

⑧Queue: SynchronousQueue

沒有容量的同步佇列,通過CAS實現併發訪問,支援FIFO和FILO。

⑨Queue: LinkedTransferQueue

JDK 7新增,單向連結串列實現的無界阻塞佇列,通過CAS實現併發訪問,佇列元素使用 FIFO(先進先出)方式。LinkedTransferQueue可以說是ConcurrentLinkedQueue、SynchronousQueue(公平模式)和LinkedBlockingQueue的超集, 它不僅僅綜合了這幾個類的功能,同時也提供了更高效的實現。

⑩List: CopyOnWriteArrayList

ArrayList 的一個執行緒安全的變體,其中所有可變操作(add、set 等等)都是通過對底層陣列進行一次新的複製來實現的。這一般需要很大的開銷,但是當遍歷操作的數量大大超過可變操作的數量時,這種方法可能比其他替代方法更 有效。在不能或不想進行同步遍歷,但又需要從併發執行緒中排除衝突時,它也很有用。

⑩①Set: CopyOnWriteArraySet

對其所有操作使用內部CopyOnWriteArrayList的Set。即將所有操作轉發至CopyOnWriteArayList來進行操作,能夠保證執行緒安全。在add時,會呼叫addIfAbsent,由於每次add時都要進行陣列遍歷,因此效能會略低於CopyOnWriteArrayList。

⑩②Set: ConcurrentSkipListSet

一個基於ConcurrentSkipListMap 的可縮放併發 NavigableSet 實現。set 的元素可以根據它們的自然順序進行排序,也可以根據建立 set 時所提供的 Comparator 進行排序,具體取決於使用的構造方法。

⑩③ Map: ConcurrentHashMap

是執行緒安全HashMap的。ConcurrentHashMap在JDK 7之前是通過Lock和segment(分段鎖)實現,JDK 8 之後改為CAS+synchronized來保證併發安全。

詳細分析請看: JUC併發集合: ConcurrentHashMap詳解, 包含了對JDK 7和JDK 8版本的原始碼分析。

⑩④ Map: ConcurrentSkipListMap

執行緒安全的有序的雜湊表(相當於執行緒安全的TreeMap);對映可以根據鍵的自然順序進行排序,也可以根據建立對映時所提供的 Comparator 進行排序,具體取決於使用的構造方法。

4、Atomic: 原子類

其基本的特性就是在多執行緒環境下,當有多個執行緒同時執行這些類的例項包含的方法時,具有排他性,即當某個執行緒進入方法,執行其中的指令時,不會被其他執行緒打斷,而別的執行緒就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待佇列中選擇一個另一個執行緒進入,這只是一種邏輯上的理解。

實際上是藉助硬體的相關指令來實現的,不會阻塞執行緒(或者說只是在硬體級別上阻塞了)。

①基礎型別:AtomicBoolean,AtomicInteger,AtomicLong

AtomicBoolean,AtomicInteger,AtomicLong是類似的,分別針對bool,interger,long的原子類。

②陣列:AtomicIntegerArray,AtomicLongArray,BooleanArray

AtomicIntegerArray,AtomicLongArray,AtomicBooleanArray是陣列原子類。

③引用:AtomicReference,AtomicMarkedReference,AtomicStampedReference

AtomicReference,AtomicMarkedReference,AtomicStampedReference是引用相關的原子類。

④FieldUpdater:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater

AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater是FieldUpdater原子類。

5、Executors: 執行緒池

執行緒池類結構關係

①介面: Executor

Executor介面提供一種將任務提交與每個任務將如何執行的機制(包括執行緒使用的細節、排程等)分離開來的方法。通常使用 Executor 而不是顯式地建立執行緒。

② ExecutorService

ExecutorService繼承自Executor介面,ExecutorService提供了管理終止的方法,以及可為跟蹤一個或多個非同步任務執行狀況而生成 Future 的方法。 可以關閉 ExecutorService,這將導致其停止接受新任務。關閉後,執行程式將最後終止,這時沒有任務在執行,也沒有任務在等待執行,並且無法提交新任務。

③ScheduledExecutorService

ScheduledExecutorService繼承自ExecutorService介面,可安排在給定的延遲後執行或定期執行的命令。

④AbstractExecutorService

AbstractExecutorService繼承自ExecutorService介面,其提供 ExecutorService 執行方法的預設實現。此類使用 newTaskFor 返回的 RunnableFuture 實現 submit、invokeAny 和 invokeAll 方法,預設情況下,RunnableFuture 是此包中提供的 FutureTask 類。

⑤FutureTask

FutureTask 為 Future 提供了基礎實現,如獲取任務執行結果(get)和取消任務(cancel)等。如果任務尚未完成,獲取任務執行結果時將會阻塞。一旦執行結束,任務就不能被重啟或取消(除非使用runAndReset執行計算)。FutureTask 常用來封裝 Callable 和 Runnable,也可以作為一個任務提交到執行緒池中執行。除了作為一個獨立的類之外,此類也提供了一些功能性函式供我們建立自定義 task 類使用。FutureTask 的執行緒安全由CAS來保證。

⑥ 核心: ThreadPoolExecutor

ThreadPoolExecutor實現了AbstractExecutorService介面,也是一個 ExecutorService,它使用可能的幾個池執行緒之一執行每個提交的任務,通常使用 Executors 工廠方法配置。 執行緒池可以解決兩個不同問題: 由於減少了每個任務呼叫的開銷,它們通常可以在執行大量非同步任務時提供增強的效能,並且還可以提供繫結和管理資源(包括執行任務集時使用的執行緒)的方法。每個 ThreadPoolExecutor 還維護著一些基本的統計資料,如完成的任務數。

⑦核心: ScheduledThreadExecutor

ScheduledThreadPoolExecutor實現ScheduledExecutorService介面,可安排在給定的延遲後執行命令,或者定期執行命令。需要多個輔助執行緒時,或者要求 ThreadPoolExecutor 具有額外的靈活性或功能時,此類要優於 Timer。)

⑧核心: Fork/Join框架

ForkJoinPool 是JDK 7加入的一個執行緒池類。Fork/Join 技術是分治演算法(Divide-and-Conquer)的並行實現,它是一項可以獲得良好的並行效能的簡單且高效的設計技術。目的是為了幫助我們更好地利用多處理器帶來的好處,使用所有可用的運算能力來提升應用的效能。)

⑨工具類: Executors

Executors是一個工具類,用其可以建立ExecutorService、ScheduledExecutorService、ThreadFactory、Callable等物件。它的使用融入到了ThreadPoolExecutor, ScheduledThreadExecutor和ForkJoinPool中。

通過以上的章節我們不難看出,JUC體系的內容也是非常龐大的,通過這篇文章能對JUC的整個體系能有所瞭解我們的目的就達到了。

接下來的文章會分明別類的對這些類和介面進行詳細的闡述分析。