Java--樂觀鎖與悲觀鎖以及樂觀鎖的一種實現(xiàn)方式-CAS

樂觀鎖或者悲觀鎖兑徘,都是一種思想宴偿,而非是真的鎖

悲觀鎖

悲觀鎖,就是不管是否發(fā)生多線程沖突胎源,只要存在這種可能涕蚤,就每次訪問都加鎖愧捕。

syncrhoized是一種獨占鎖次绘,即:占用該鎖的線程才可以執(zhí)行,申請該鎖的線程就只能掛起等待禾进,直到占用鎖的線程釋放鎖才喚醒艇拍,拿到鎖并執(zhí)行卸夕。由于在進程掛起和恢復(fù)執(zhí)行過程中存在著很大的開銷,并且當(dāng)一個線程正在等待鎖時个初,它不能做任何事。所以syncrhoized是一種悲觀鎖,凡是用syncrhoized加了鎖的多線程之間都會因鎖的爭奪結(jié)果導(dǎo)致掛起瓢省、喚醒等開銷。

樂觀鎖

樂觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會產(chǎn)生并發(fā)沖突馒胆,所以在數(shù)據(jù)進行提交更新的時候,才會正式對數(shù)據(jù)是否產(chǎn)生并發(fā)沖突進行檢測型雳,如果發(fā)現(xiàn)并發(fā)沖突了,則讓返回用戶錯誤的信息冤荆,讓用戶決定如何去做乌妒。

上面提到的樂觀鎖的概念中其實已經(jīng)闡述了它的具體實現(xiàn)細節(jié):主要就是兩個步驟:沖突檢測和數(shù)據(jù)更新芥被。其實現(xiàn)方式有一種比較典型的就是 Compare and Swap ( CAS )。

CAS

CAS是樂觀鎖技術(shù)匹中,當(dāng)多個線程嘗試使用CAS同時更新同一個變量時顶捷,只有其中一個線程能更新變量的值,而其它線程都失敗重虑,失敗的線程并不會被掛起,而是被告知這次競爭中失敗提针,并可以再次嘗試。
CAS: 全稱Compare and swap揖曾,字面意思:”比較并交換“,一個 CAS 涉及到以下操作:

我們假設(shè)內(nèi)存中的原數(shù)據(jù)V奴拦,舊的預(yù)期值A(chǔ)绿鸣,需要修改的新值B。

  • 比較 A 與 V 是否相等擎厢。(比較)
  • 如果比較相等,將 B 寫入 V厘惦。(交換)
  • 返回操作是否成功。
Java對CAS的支持

在JDK1.5 中新增java.util.concurrent(J.U.C)就是建立在CAS之上的羡玛。相對于對于synchronized這種阻塞算法浓若,CAS是非阻塞算法的一種常見實現(xiàn)。所以J.U.C在性能上有了很大的提升碌上。

我們以java.util.concurrent中的AtomicInteger為例盔性,看一下在不使用鎖的情況下是如何保證線程安全的后豫。主要理解getAndIncrement方法挫酿,該方法的作用相當(dāng)于 ++i 操作早龟。

public class AtomicInteger extends Number implements java.io.Serializable {  

        private volatile int value;  

    public final int get() {  
        return value;  
    }  

    public final int getAndIncrement() {  
        for (;;) {  
            int current = get();  
            int next = current + 1;  
            if (compareAndSet(current, next))  
                return current;  
        }  
    }  

    public final boolean compareAndSet(int expect, int update) {  
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
    }  
}

這里面有一個知識點如:unsafe,先帶著這個疑問我先說代碼意思

在沒有鎖的機制下需要字段value要借助volatile,保證線程間的數(shù)據(jù)是可見的饵骨。這樣在獲取變量的值的時候才能直接讀取翘悉。然后來看看++i是怎么做到的

getAndIncrement采用了CAS操作,每次從內(nèi)存中讀取數(shù)據(jù)然后將此數(shù)據(jù)和+1后的結(jié)果進行CAS操作居触,如果成功就返回結(jié)果妖混,否則重試直到成功為止。而compareAndSet利用JNI來完成CPU指令的操作轮洋。

AtomicInteger中就利用了volatile可見性和CAS的原子性實現(xiàn)了多線程安全

接著Unsafe是啥呢 他就有CAS的功能

Unsafe

java不能直接訪問操作系統(tǒng)底層弊予,而是通過本地方法來訪問。Unsafe類提供了硬件級別的原子操作

主要提供了以下功能:

1、通過Unsafe類可以分配內(nèi)存讼育,可以釋放內(nèi)存;

類中提供的3個本地方法allocateMemory逆航、reallocateMemory蓉坎、freeMemory分別用于分配內(nèi)存罐监,擴充內(nèi)存和釋放內(nèi)存,與C語言中的3個方法對應(yīng)柏锄。

2评架、可以定位對象某字段的內(nèi)存位置塘砸,也可以修改對象的字段值蛉迹,即使它是私有的;
public native long allocateMemory(long l);
public native long reallocateMemory(long l, long l1);
public native void freeMemory(long l);
3祟蚀、掛起與恢復(fù)

將一個線程進行掛起是通過park方法實現(xiàn)的肺孵,調(diào)用 park后匀借,線程將一直阻塞直到超時或者中斷等條件出現(xiàn)瑰艘。unpark可以終止一個掛起的線程,使其恢復(fù)正常键兜。整個并發(fā)框架中對線程的掛起操作被封裝在 LockSupport類中,LockSupport類中有各種版本pack方法尺棋,但最終都調(diào)用了Unsafe.park()方法封锉。

public class LockSupport {
    public static void unpark(Thread thread) {
        if (thread != null)
            unsafe.unpark(thread);
    }

    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        unsafe.park(false, 0L);
        setBlocker(t, null);
    }

    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            unsafe.park(false, nanos);
            setBlocker(t, null);
        }
    }

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        unsafe.park(true, deadline);
        setBlocker(t, null);
    }

    public static void park() {
        unsafe.park(false, 0L);
    }

    public static void parkNanos(long nanos) {
        if (nanos > 0)
            unsafe.park(false, nanos);
    }

    public static void parkUntil(long deadline) {
        unsafe.park(true, deadline);
    }
}
4、CAS操作

是通過compareAndSwapXXX方法實現(xiàn)的

/**
* 比較obj的offset處內(nèi)存位置中的值和期望的值膘螟,如果相同則更新成福。此更新是不可中斷的。
* 
* @param obj 需要更新的對象
* @param offset obj中整型field的偏移量
* @param expect 希望field中存在的值
* @param update 如果期望值expect與field的當(dāng)前值相同荆残,設(shè)置filed的值為這個新值
* @return 如果field的值被更改返回true
*/
public native boolean compareAndSwapInt(Object obj, long offset, int expect, int update);

CAS操作有3個操作數(shù)奴艾,內(nèi)存值M,預(yù)期值E内斯,新值U握侧,如果M==E,則將內(nèi)存值修改為B嘿期,否則啥都不做

CAS的隱藏問題ABA

CAS算法實現(xiàn)一個重要前提需要取出內(nèi)存中某時刻的數(shù)據(jù)品擎,而在下時刻比較并替換,那么在這個時間差類會導(dǎo)致數(shù)據(jù)的變化备徐。

比如說一個線程one從內(nèi)存位置V中取出A萄传,這時候另一個線程two也從內(nèi)存中取出A,并且two進行了一些操作變成了B蜜猾,然后two又將V位置的數(shù)據(jù)變成A秀菱,這時候線程one進行CAS操作發(fā)現(xiàn)內(nèi)存中仍然是A,然后one操作成功蹭睡。盡管線程one的CAS操作成功衍菱,但是不代表這個過程就是沒有問題的。

部分樂觀鎖的實現(xiàn)是通過版本號(version)的方式來解決ABA問題肩豁,樂觀鎖每次在執(zhí)行數(shù)據(jù)的修改操作時脊串,都會帶上一個版本號辫呻,一旦版本號和數(shù)據(jù)的版本號一致就可以執(zhí)行修改操作并對版本號執(zhí)行+1操作,否則就執(zhí)行失敗琼锋。因為每次操作的版本號都會隨之增加放闺,所以不會出現(xiàn)ABA問題,因為版本號只會增加不會減少缕坎。

https://www.cnblogs.com/qjjazry/p/6581568.html

https://blog.csdn.net/zhengchao1991/article/details/56013248

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怖侦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谜叹,更是在濱河造成了極大的恐慌匾寝,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件荷腊,死亡現(xiàn)場離奇詭異旗吁,居然都是意外死亡,警方通過查閱死者的電腦和手機停局,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門很钓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人董栽,你說我怎么就攤上這事码倦。” “怎么了锭碳?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵袁稽,是天一觀的道長。 經(jīng)常有香客問我擒抛,道長推汽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任歧沪,我火速辦了婚禮歹撒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诊胞。我一直安慰自己暖夭,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布撵孤。 她就那樣靜靜地躺著迈着,像睡著了一般。 火紅的嫁衣襯著肌膚如雪邪码。 梳的紋絲不亂的頭發(fā)上裕菠,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機與錄音闭专,去河邊找鬼奴潘。 笑死旧烧,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的萤彩。 我是一名探鬼主播粪滤,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼斧拍,長吁一口氣:“原來是場噩夢啊……” “哼雀扶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起肆汹,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤愚墓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后昂勉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浪册,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年岗照,在試婚紗的時候發(fā)現(xiàn)自己被綠了村象。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡攒至,死狀恐怖厚者,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情迫吐,我是刑警寧澤库菲,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站志膀,受9級特大地震影響熙宇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜溉浙,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一烫止、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧戳稽,春花似錦烈拒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赊时,卻和暖如春吨铸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祖秒。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工诞吱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舟奠,地道東北人。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓房维,卻偏偏與公主長得像沼瘫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子咙俩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,652評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 首先介紹一些樂觀鎖與悲觀鎖: 悲觀鎖:總是假設(shè)最壞的情況耿戚,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會修改,所以每次在拿數(shù)據(jù)的時...
    奧莉安娜的棒棒糖閱讀 1,584評論 0 19
  • Java8張圖 11阿趁、字符串不變性 12膜蛔、equals()方法、hashCode()方法的區(qū)別 13脖阵、...
    Miley_MOJIE閱讀 3,701評論 0 11
  • 文/邱晨 ╲本篇屬狗屎文皂股,智慧不夠慎入╱ 開腦洞”者如同盜墓者,都是在平淡無奇里, 運用明銳的才華眼,發(fā)現(xiàn)無限瑰麗...
    懶爺邱閱讀 377評論 1 3
  • 《非暴力溝通》P140——P141表達憤怒的四個步驟現(xiàn)在讓我們看看非暴力溝通表達憤怒的具體步驟。首先命黔,停下來呜呐,除了...
    Fly_Catkin閱讀 236評論 0 0
  • 馬蘭是一個地名,在新疆南部的天山腳下悍募,曾以“原子彈”的爆炸而聞名中外蘑辑。那里生長著一種太陽曬不死的花兒——馬蘭花。 ...
    武俊祥閱讀 1,838評論 0 2