悲觀者與樂觀者的做事方式完全不一樣,悲觀者的人生觀是一件事情我必須要百分之百完全控制才會去做,否則就認為這件事情一定會出問題;而樂觀者的人生觀則相反,凡事不管最終結果如何,他都會先嘗試去做,大不了最后不成功。這就是悲觀鎖與樂觀鎖的區別,悲觀鎖會把整個對象加鎖占為自有后才去做操作,樂觀鎖不獲取鎖直接做操作,然后通過一定檢測手段決定是否更新數據。這一節將對樂觀鎖進行深入探討。
上節討論的Synchronized互斥鎖屬于悲觀鎖,它有一個明顯的缺點,它不管數據存不存在競爭都加鎖,隨著并發量增加,且如果鎖的時間比較長,其性能開銷將會變得很大。有沒有辦法解決這個問題?答案是基于沖突檢測的樂觀鎖。這種模式下,已經沒有所謂的鎖概念了,每條線程都直接先去執行操作,計算完成后檢測是否與其他線程存在共享數據競爭,如果沒有則讓此操作成功,如果存在共享數據競爭則可能不斷地重新執行操作和檢測,直到成功為止,可叫CAS自旋。
樂觀鎖的核心算法是CAS(Compareand Swap,比較并交換),它涉及到三個操作數:內存值、預期值、新值。當且僅當預期值和內存值相等時才將內存值修改為新值。這樣處理的邏輯是,首先檢查某塊內存的值是否跟之前我讀取時的一樣,如不一樣則表示期間此內存值已經被別的線程更改過,舍棄本次操作,否則說明期間沒有其他線程對此內存值操作,可以把新值設置給此塊內存。如圖2-5-4-1,有兩個線程可能會差不多同時對某內存操作,線程二先讀取某內存值作為預期值,執行到某處時線程二決定將新值設置到內存塊中,如果線程一在此期間修改了內存塊,則通過CAS即可以檢測出來,假如檢測沒問題則線程二將新值賦予內存塊。

圖2-5-4-1
假如你足夠細心你可能會發現一個疑問,比較和交換,從字面上就有兩個操作了,更別說實際CAS可能會有更多的執行指令,他們是原子性的嗎?如果非原子性又怎么保證CAS操作期間出現并發帶來的問題?我是不是需要用上節提到的互斥鎖來保證他的原子性操作?CAS肯定是具有原子性的,不然就談不上在并發中使用了,但這個原子性是由CPU硬件指令實現保證的,即使用JNI調用native方法調用由C++編寫的硬件級別指令,jdk中提供了Unsafe類執行這些操作。另外,你可能想著CAS是通過互斥鎖來實現原子性的,這樣確實能實現,但用這種方式來保證原子性顯示毫無意義。下面一個偽代碼加深對CAS的理解:
public class AtomicInt {
??private volatile int value;
??public final int get() {
??????return value;
?? }
publicfinal int getAndIncrement() {
??????for (;;) {
??????????int current = get();
??????????int next = current + 1;
??????????if (compareAndSet(current, next))
?????????????? return current;
??????}
?? }
??
??public final boolean compareAndSet(int expect, int update) {
??? Unsafe類提供的硬件級別的compareAndSwapInt方法;
?? }
}
其中最重要的方法是getAndIncrement方法,它里面實現了基于CAS的自旋。
現在已經了解樂觀鎖及CAS相關機制,樂觀鎖避免了悲觀鎖獨占對象的現象,同時也提高了并發性能,但它也有缺點:
①??觀鎖只能保證一個共享變量的原子操作。如上例子,自旋過程中只能保證value變量的原子性,這時如果多一個或幾個變量,樂觀鎖將變得力不從心,但互斥鎖能輕易解決,不管對象數量多少及對象顆粒度大小。
②??長時間自旋可能導致開銷大。假如CAS長時間不成功而一直自旋,會給CPU帶來很大的開銷。
③??ABA問題。CAS的核心思想是通過比對內存值與預期值是否一樣而判斷內存值是否被改過,但這個判斷邏輯不嚴謹,假如內存值原來是A,后來被一條線程改為B,最后又被改成了A,則CAS認為此內存值并沒有發生改變,但實際上是有被其他線程改過的,這種情況對依賴過程值的情景的運算結果影響很大。解決的思路是引入版本號,每次變量更新都把版本號加一。
樂觀鎖是對悲觀鎖的改進,雖然它也有缺點,但它確實已經成為提高并發性能的主要手段,而且jdk中的并發包也大量使用基于CAS的樂觀鎖。
喜歡研究java的同學可以交個朋友,下面是本人的微信號:

- 前言
- 悲觀的并發策略——Synchronized互斥鎖
- 樂觀的并發策略——基于CAS的自旋
- Java并發框架——AQS之原子性如何保證?
- Java并發框架——AQS之如何使用AQS構建同步器
- Java多線程模型
- Java并發框架——什么是AQS框架
- Java多線程的調度策略
- Java并發框架——AQS中斷的支持
- Java線程狀態
- 多線程之Java線程阻塞與喚醒
- Java并發框架——AQS之阻塞與喚醒
- Java并發框架——AQS阻塞隊列管理(一)——自旋鎖
- Java并發框架——AQS阻塞隊列管理(二)——自旋鎖優化
- Java并發框架——AQS阻塞隊列管理(三)——CLH鎖改造
- Java并發框架——AQS超時機制
- Java并發框架——同步狀態的管理
- Java并發框架——公平性
- Java并發——線程池原理