併發程式設計 — CAS 原理詳解
在JDK1.5引入的 J.U.C包中的原子類以及Lock等都是基於 volatile 關鍵結合 CAS 操作實現的,為了能夠搞明白 原子類以及 Lock鎖的原理首先要了解 volatile 原理以及 CAS原理,上篇文章我們說了volatile關鍵字,這篇我們們就聊聊 什麼是 CAS 操作。
悲觀鎖與樂觀鎖
在說CAS操作之前我們們先說一下什麼是悲觀鎖和樂觀鎖。
悲觀鎖
悲觀鎖就是總是加鎖最壞的情況,所以每次去拿資料時都認為別人會修改,所以每次操作共享資料時都會上鎖,這樣當別人需要訪問共享資料時,就必須阻塞等待,直到它拿到鎖。傳統的關係型資料庫裡面就用到了很多這種鎖機制,比如行鎖、表鎖等,都是在操作之前先加上鎖。在Java中 synchronized 和 ReentrantLock 等都是獨佔的,也就是悲觀鎖。
悲觀鎖可以理解為就是 “總有刁民想害朕”。
樂觀鎖
樂觀鎖就是總是情況最好的,每次去拿共享資料的時候認為別人不會修改,所以不會上鎖,但是在更新資料的時候會判斷一下在此期間別人是否更改了資料。一般可以通過版本號和 CAS演算法實現。樂觀鎖適用於讀多寫少的場景,這樣可以提供吞吐量。
什麼是CAS
CAS 即 Compare And Swap (比較與交換),是一種有名的無鎖演算法。CAS演算法主要涉及到三個引數:
- 主記憶體中存放的的值V,也就是執行緒共享的值。
- 執行緒上次從記憶體中讀取的V值 A,執行緒私有的,存放線上程棧幀中,或者CPU快取中的值。
- 需要寫入主記憶體改些V的值B,執行緒對A計算後的值。
大致流程如下所示:
CAS帶來的三大問題
ABA問題
因為CAS需要在操作值的時候,檢查值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼CAS進行檢查的時候發現它的值沒有發生變化,但是實際上卻變化了。ABA問題的解決思路就是使用版本號。在變數前面加上版本號,每次變數更新的時候把版本號加1,那麼A->B->A就會變成1A->2B->3A。從Java 1.5開始,JDK的Atomic包裡提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法的作用是首先檢查當前引用是否等於預期引用,並且檢查當前的標誌是否等於預期標誌,如果全部相等,則以原子方式將該應用和該標誌的值設定為給定的更新值。
開銷大的問題
自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷,如果JVM能支援處理器提供的pause指令,那麼效率會有一定的提升。pause指令有兩個作用:
第一,它可以延遲流水線執行指令(de-pipeline),使CPU不會消耗過多的執行資源,延遲的時間取決於具體實現的版本,在一些處理器上延遲時間是零;
第二,它可以避免在迴圈的時候因記憶體順序衝突(Memory Order Violation)而引起CPU流水線被清空,從而提高CPU的實行效率。
只能保證一個共享變數的原子操作
當對一個共享變數執行操作時,我們可以使用迴圈CAS的方式來保證原子操作,但是對多個共享變數操作時,迴圈CAS就無法保證操作的原子性,這個時候可以用鎖。還有一個取巧的辦法,就是把多個共享變數合併成一個共享變數來操作。比如,有兩個共享變數i=2,j=a,合併一下ji=2a,然後用CAS來操作ij。從Java 1.5開始,JDK提供了AtomicReference類來保證引用物件之前的原子性,就可以把多個變數放在一個物件裡來進行CAS操作。
總結
本篇文章首先介紹了什麼是悲觀鎖和樂觀鎖,然後介紹了 CAS 原理,CAS 就是 比較並交換,也就是在寫入時先比較 主記憶體中值是否跟之前讀取的舊值相等,如果相等就更新,如果不相等就再次讀取計算,迴圈往復。CAS 在提高效能的同時,也帶來了三大問題。明白三大問題能夠更好的使用 CAS操作,以及後續的 原子操作類。
相關文章
- 併發程式設計之 CAS 的原理程式設計
- Java併發程式設計-CASJava程式設計
- CAS原理分析:併發程式設計核心中的核心你瞭解多少?程式設計
- 譯文《Java併發程式設計之CAS》Java程式設計
- Go併發程式設計之美-CAS操作Go程式設計
- Java併發程式設計之Java CAS操作Java程式設計
- 併發程式設計——IO模型詳解程式設計模型
- 併發程式設計基礎(二)—— ThreadLocal及CAS基本原理剖析程式設計thread
- 併發程式設計ConcurrentLinkedQueue使用示例詳解程式設計
- Java 併發程式設計(一) → LockSupport 詳解Java程式設計
- 併發程式設計——詳解 AQS CLH 鎖程式設計AQS
- Java併發程式設計(一)Thread詳解Java程式設計thread
- Java CAS 原理詳解Java
- 併發程式設計 join原理程式設計
- JUC併發程式設計詳解(通俗易懂)程式設計
- Java併發程式設計系列之Semaphore詳解Java程式設計
- java併發程式設計 | 執行緒詳解Java程式設計執行緒
- [併發程式設計]-關於 CAS 的幾個問題程式設計
- Java 併發程式設計 | 執行緒池詳解Java程式設計執行緒
- Java併發程式設計中的鎖機制詳解Java程式設計
- java併發程式設計 | 鎖詳解:AQS,Lock,ReentrantLock,ReentrantReadWriteLockJava程式設計AQSReentrantLock
- 併發程式設計之顯式鎖原理程式設計
- 併發程式設計 —— ConcurrentHashMap size 方法原理分析程式設計HashMap
- Java併發程式設計(07):Fork/Join框架機制詳解Java程式設計框架
- Java併發/多執行緒-CAS原理分析Java執行緒
- CAS原理分析及ABA問題詳解
- 《java併發程式設計的藝術》併發底層實現原理Java程式設計
- Java併發程式設計(06):Lock機制下API用法詳解Java程式設計API
- Java併發程式設計的藝術,解讀併發程式設計的優缺點Java程式設計
- 併發程式設計程式設計
- 併發程式設計——多執行緒計數的更優解:LongAdder原理分析程式設計執行緒
- 併發程式設計原理學習:synchronized關鍵字程式設計synchronized
- Go 語言併發程式設計之互斥鎖詳解 sync.MutexGo程式設計Mutex
- java併發程式設計系列:java併發程式設計背景知識Java程式設計
- 併發程式設計基礎底層原理學習(四)程式設計
- 【Java併發程式設計】Synchronized關鍵字實現原理Java程式設計synchronized
- 併發程式設計基礎底層原理學習(二)程式設計
- 併發程式設計基礎底層原理學習(一)程式設計