Java多線程併發06——CAS與AQS

在進行更近一步的瞭解Java鎖的知識以前,咱們須要先了解與鎖有關的兩個概念 CAS 與 AQS。關注個人公衆號「Java面典」瞭解更多 Java 相關知識點。html

CAS(Compare And Swap/Set)

概念

CAS函數,是比較並交換函數,它是原子操做函數。算法

原理

CAS 是基於樂觀鎖的原理進行操做的。它老是認爲本身能夠成功完成操做。當多個線程同時使用 CAS 操做一個變量時,只有一個會勝出,併成功更新,其他均會失敗。失敗的線程不會被掛起,僅是被告知失敗,而且容許再次嘗試或放棄操做。多線程

實現

  1. 構造:CAS 包含 3 個參數CAS(V,E,N)。V 表示要更新的變量(內存值),E 表示預期值(舊的),N 表示新值;
  2. 比較並交換:當且僅當 V 值等於 E 值時,纔會將 V 的值設爲 N;若是 V 值和 E 值不一樣,則說明已經有其餘線程作了更新,當前線程什麼都不作;
  3. 返回:CAS 返回當前 V 的真實值。

ABA 問題

CAS 會致使「ABA 問題」。CAS 算法實現一個重要前提須要取出內存中某時刻的數據,而在下時刻比較並替換,那麼在這個時間差內會致使數據的變化。併發

ABA 例子:好比說一個線程 one 從內存位置 V 中取出 A,這時候另外一個線程 two 也從內存中取出 A,而且two 進行了一些操做變成了 B,而後 two 又將 V 位置的數據變成 A,這時候線程 one 進行 CAS 操做發現內存中仍然是 A,而後 one 操做成功。儘管線程 one 的 CAS 操做成功,可是不表明這個過程就是沒有問題的。框架

ABA 解決方案:部分樂觀鎖的實現是經過版本號(version)的方式來解決 ABA 問題,樂觀鎖每次在執行數據的修改操做時,都會帶上一個版本號,一旦版本號和數據的版本號一致就能夠執行修改操做並對版本號執行+1 操做,不然就執行失敗。由於每次操做的版本號都會隨之增長,因此不會出現 ABA 問題,由於版本號只會增長不會減小。函數

AQS

概念

AQS即AbstractQueuedSynchronizer (抽象的隊列式的同步器),AQS 定義了一套多線程訪問共享資源的同步器框架,許多同步類實現都依賴於它,如經常使用的ReentrantLock/Semaphore/CountDownLatch。ui

AQS模型.PNG

AQS 維護了一個 volatile int state(表明共享資源)和一個 FIFO 線程等待隊列(多線程爭用資源被阻塞時會進入此隊列)。這裏 volatile 是核心關鍵詞,具體 volatile 的語義,在此不述。state 的訪問方式有三種:
getState()
setState()
compareAndSetState()線程

原理(state 資源狀態計數)

計數器:同步器的實現是 ABS 核心,以 ReentrantLock 爲例,state 初始化爲 0,表示未鎖定狀態。A 線程 lock()時,會調用 tryAcquire() 獨佔該鎖並將 state+1。此後,其餘線程再 tryAcquire() 時就會失敗,直到 A 線程 unlock() 到 state=0(即釋放鎖)爲止,其它線程纔有機會獲取該鎖。code

可重入鎖:在 A 線程釋放鎖以前,A 線程本身是能夠重複獲取此鎖的(state 會累加),這就是可重入鎖的實現。
但要注意,獲取多少次就要釋放多麼次,這樣才能保證 state 是能回到零態的htm

資源共享方式

AQS 定義了兩種資源共享方式:

  • Exclusive 獨佔資源:Exclusive(獨佔,只有一個線程能執行,如 ReentrantLock);
  • Share 共享資源:Share(共享,多個線程可同時執行,如Semaphore/CountDownLatch)。

通常來講,自定義同步器要麼是獨佔方法,要麼是共享方式,他們也只需實現 tryAcquire-tryRelease、tryAcquireShared-tryReleaseShared 中的一種便可。但 AQS 也支持自定義同步器同時實現獨佔和共享兩種方式,如 ReentrantReadWriteLock.

實現方式

AQS 只是一個框架,具體資源的獲取/釋放方式交由自定義同步器去實現。自定義同步器實現時主要實現如下幾種方法:
1.isHeldExclusively():該線程是否正在獨佔資源。只有用到 condition 才須要去實現它。
2.tryAcquire(int):獨佔方式,嘗試獲取資源。成功則返回 true,失敗則返回 false。
3.tryRelease(int):獨佔方式,嘗試釋放資源。成功則返回 true,失敗則返回 false。
4.tryAcquireShared(int):共享方式,嘗試獲取資源。負數表示失敗;0 表示成功,但沒有剩餘可用資源;正數表示成功,且有剩餘資源。
5.tryReleaseShared(int):共享方式,嘗試釋放資源。若是釋放後容許喚醒後續等待結點返回true,不然返回 false。

多線程與併發系列推薦

Java多線程併發05——那麼多的鎖你都瞭解了嗎

Java多線程併發04——合理使用線程池

Java多線程併發03——什麼是線程上下文,線程是如何調度的

Java多線程併發02——線程的生命週期與經常使用方法,你都掌握了嗎

Java多線程併發01——線程的建立與終止,你會幾種方式

相關文章
相關標籤/搜索