Week 1 - Java 多線程 - CAS

前言

學習狀況記錄java

  • 時間:week 1
  • SMART子目標 :Java 多線程

記錄在學習線程安全知識點中,關於CAS的有關知識點。算法

線程安全是指:多個線程無論以何種方式訪問某個類,而且在主調代碼中不須要進行同步,都能表現正確的行爲。安全

常見的線程安全實現方法分爲不可變對象、線程互斥同步、非阻塞同步、線程本地存儲等方案,本文要講的就是非阻塞同步中的核心CAS.多線程

非阻塞同步

從處理問題的方式上說,互斥同步屬於一種悲觀的併發策略。併發

隨着硬件指令集的發展,咱們能夠採用基於衝突檢查的樂觀併發策略,通俗地說,就是先行操做,若是沒有其餘線程爭用共享數據,那操做就成功了;若是共享數據有爭用,產生了衝突,那就再採起其餘的補償措施(最多見的補償措施就是不斷地重試,直到成功爲止),這種樂觀的併發策略的許多實現偶讀不須要把線程掛起,所以這種同步操做稱爲非阻塞同步。less

CAS

樂觀鎖須要操做和衝突檢測這兩個步驟具有原子性,這裏就不能再使用互斥同步來保證了,只能靠硬件來完成硬件支持的原子性操做最典型的是:比較並交換(Compare-and-Swap,CAS)CAS 指令須要有 3 個操做數,分別是內存地址 V、舊的預期值 A 和新值 B。當執行操做時,只有當 V 的值等於 A,纔將 V 的值更新爲 B。ide

各類Atomic開頭的原子類,內部都應用到了CAS。就拿AtomicInteger爲例。性能

J.U.C 包裏面的原子類 AtomicInteger 的方法調用了 Unsafe 類的 CAS 操做。學習

看看AtomicInteger對象一次自增,CAS起了什麼做用,如下代碼是 incrementAndGet() 的源碼,能夠看到內部調用了 Unsafe 對象的 getAndAddInt()spa

如下代碼是 getAndAddInt()源碼,var1 指示對象內存地址,var2指示該字段相對對象內存地址的偏移,var4 指示操做須要加的數值,這裏爲 1。經過 getIntVolatile(var1, var2) 獲得舊的預期值,經過調用 compareAndSwapInt() 來進行 CAS 比較若是該字段內存地址中的值等於 var5,那麼就更新內存地址爲 var1+var2 的變量爲 var5+var4

compareAndSwapInt(var1, var2, var5, var5 + var4 其實換成compareAndSwapInt(obj, offset, expect, update)比較清楚,意思就是若是obj內的valueexpect相等,就證實沒有其餘線程改變過這個變量,那麼就更新它爲update,若是這一步的CAS沒有成功,那就採用自旋的方式繼續進行CAS操做,取出乍一看這也是兩個步驟了啊,其實在JNI裏是藉助於一個CPU指令完成的。因此仍是原子操做。

CAS 的問題

  • ABA問題

    • 描述:若是一個變量初次讀取的時候是 A 值,它的值被改爲了 B,後來又被改回爲 A,那 CAS 操做就會誤認爲它歷來沒有被改變過。
    • 解決方案:J.U.C 包提供了一個帶有標記的原子引用類 AtomicStampedReference 來解決這個問題,它能夠經過控制變量值的版原本保證 CAS 的正確性。大部分狀況下 ABA 問題不會影響程序併發的正確性,若是真的須要解決 ABA 問題,改用傳統的互斥同步可能會比原子類更高效
  • 循環時間長開銷大

    • 自旋CAS(也就是不成功就一直循環執行直到成功)若是長時間不成功,會給CPU帶來比較大的執行開銷。
  • 只能保證一個共享變量的原子操做

    • CAS 只對單個共享變量有效,當操做涉及跨多個共享變量時CAS 無效。可是從 JDK 1.5開始,提供了AtomicReference類來保證引用對象之間的原子性,你能夠把多個變量放在一個對象裏來進行 CAS 操做.因此咱們可使用鎖或者利用AtomicReference類把多個共享變量合併成一個共享變量來操做。

CAS與synchronized的使用情景

  • 簡單的來講CAS適用於寫比較少的狀況下(多讀場景,衝突通常較少)
  • synchronized適用於寫比較多的狀況下(多寫場景,衝突通常較多)
  • 對於資源競爭較少(線程衝突較輕)的狀況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態內核態間的切換操做額外浪費消耗cpu資源;而CAS基於硬件實現,不須要進入內核,不須要切換線程,操做自旋概率較少,所以能夠得到更高的性能。
  • 對於資源競爭嚴重(線程衝突嚴重)的狀況,CAS自旋的機率會比較大,從而浪費更多的CPU資源,效率低於synchronized。

CAS 的應用

使用 CAS 原子指令來處理對數據的併發訪問,這是非阻塞算法得以實現的基礎。關於非阻塞算法是屬於J.U.C中併發容器部分的知識,屬於比較難的內容。目前先引用幾篇文章。做爲記錄。

  1. 非阻塞算法在併發容器中的實現
  2. 非阻塞同步算法實戰(一)
  3. 非阻塞同步算法實戰(二)-BoundlessCyclicBarrier
  4. 非阻塞同步算法實戰(三)-LatestResultsProvider

參考

  1. 《深刻理解Java虛擬機》
  2. https://www.ibm.com/developer...
相關文章
相關標籤/搜索