java併發 CAS原理學習 樂觀鎖 悲觀鎖

2021-09-24 02:07:15 字數 1545 閱讀 9818

在之前文章中驗證了在多執行緒場景下,cas可以保證共享變數的原子性。

此篇文章主要記錄一下cas原理的學習感悟。

在一般情況下,為保證資料安全性,我們可以採用synchronized修飾變數或者修飾方法。也就是說在同一時間只有乙個執行緒能修改共享變數或者訪問這個方法,其它執行緒都要等待。但是這樣的話,也就相當於了單執行緒,失去了多執行緒的優勢。

cas的原理有些類似於悲觀鎖;

了解cas之前,先了解一下悲觀鎖,樂觀鎖;

悲觀鎖:也就是往最壞的情況下考慮,假設會傳送併發衝突的問題,所以當某個執行緒獲取到共享資源時,會阻止別的執行緒獲取共享資源。所以也可以稱為  獨佔鎖或者互斥鎖。比如synchronized同步鎖。

樂觀鎖:故名思意,假設不會發生併發衝突的情況。只有在最後更新修改共享資源時,才會去判斷一下在從主存中拿到資料到修改資料這段時間內有沒有別的執行緒修改了這個共享資源。如果發生了衝突就重試,如果沒有衝突,就更新成功。cas是樂觀鎖的一種實現方式。

【悲觀鎖會阻塞其它執行緒,樂觀鎖不會阻塞其它執行緒,如果發生衝突,會採用迴圈的方式一直重試,直至成功】

cas主要通過三個值實現 

v:當前記憶體值

a:預期值

b:期待更新的值

舉個栗子解釋一下上面三個引數:

場景:現在有兩個執行緒同時更改共享變數56【此時當前記憶體值(v)】,兩個執行緒各自對共享變數進行+1的操作【期待更新的值(b)就為1】

因為執行緒1,執行緒2 同時訪問同乙個共享變數56,都會將主存中的值拷貝到自己的工作記憶體中【執行緒1,執行緒2的預期值(a)都是56】

對於執行緒2來說,此時記憶體值變成了57,和預期值56不一致。所以就會操作失敗?操作失敗怎麼處理呢?重新獲取記憶體中的最新值,這時候執行緒2的預期值就變成了57,和記憶體值相等。再進行更新操作。

此操作會採用迴圈的方式直至更新成功;

public final int getandaddint(object obj, long valueoffset, int var)  while(!this.compareandswapint(obj, valueoffset, expect, expect + var));

// 返回當前執行緒在更改value成功後的,value變數原先值。並不是更改後的值

return expect;

}

就是指當兩者進行比較時,如果相等,則證明共享資料沒有被修改,替換成新值,然後繼續往下執行;如果不相等,說明共享資料已經被修改,放棄已經所做的操作,然後重新執行剛才的操作。容易看出 cas 操作是基於共享資料不會被修改的假設,採用了類似於資料庫的commit-retry 的模式。當同步衝突出現的機會很少時,這種假設能帶來較大的效能提公升。

由此可見,atomicinteger.incrementandget的實現用了樂觀鎖技術,呼叫了類sun.misc.unsafe庫裡面的 cas演算法,用cpu指令來實現無鎖自增。所以,atomicinteger.incrementandget的自增比用synchronized的鎖效率倍增。

參考:

樂觀的併發策略 基於CAS的自旋

悲觀者與樂觀者的做事方式完全不一樣,悲觀者的人生觀是一件事情我必須要百分之百完全控制才會去做,否則就認為這件事情一定會出問題 而樂觀者的人生觀則相反,凡事不管最終結果如何,他都會先嘗試去做,大不了最後不成功。這就是悲觀鎖與樂觀鎖的區別,悲觀鎖會把整個物件加鎖佔為自有後才去做操作,樂觀鎖不獲取鎖直接做...

java中樂觀鎖CAS操作詳解

樂觀鎖就是假設所有執行緒訪問共享資源都不會出現衝突,由於不會出現衝突所以就不會也不需要去阻塞其他執行緒。因此執行緒不會出現阻塞等待的狀態。但是一旦發生衝突時,無鎖操作,會使用cas操作來鑑別執行緒是否出現衝突,出現衝突了就重複嘗試,直到沒有衝突為止。cas操作就是compare and swap比較...

併發程式設計基礎底層原理學習(二)

程序 程序就是應用程式在記憶體中分配的空間,也就是正在執行的程式,各個程序之間互不干擾。同時程序儲存著程式每乙個時刻執行的狀態。程序的兩個基本元素是程式 和與 關聯的資料集。程序執行的任意時刻包含了以下元素 狀態 若程序正在執行,則程序處於執行態 優先順序 相對於其他程序的優先順序 上下文資料 程序...