更新時間:2022年09月29日14時42分 來源:傳智教育 瀏覽次數(shù):
CAS(Compare and Swap),即比較并替換,是用于實(shí)現(xiàn)多線程同步的原子指令,是用于實(shí)現(xiàn)多線程同步的原子指令。
執(zhí)行函數(shù):CAS(V,E,N)
其包含3個參數(shù):
· V表示要更新的變量
· E表示預(yù)期值
· N表示新值
假定有兩個操作A和B,如果從執(zhí)行A的線程來看,當(dāng)另一個線程執(zhí)行B時,要么將B全部執(zhí)行完,要么完全不執(zhí)行B,那么A和B對彼此來說是原子的。
實(shí)現(xiàn)原子操作可以使用鎖,鎖機(jī)制,滿足基本的需求是沒有問題的了,但是有的時候我們的需求并非這么簡單,我們需要更有效,更加靈活的機(jī)制,synchronized關(guān)鍵字是基于阻塞的鎖機(jī)制,也就是說當(dāng)一個線程擁有鎖的時候,訪問同一資源的其它線程需要等待,直到該線程釋放鎖。
這里會有些問題:首先,如果被阻塞的線程優(yōu)先級很高很重要怎么辦?其次,如果獲得鎖的線程一直不釋放鎖怎么辦?(這種情況是非常糟糕的)。還有一種情況,如果有大量的線程來競爭資源,那CPU將會花費(fèi)大量的時間和資源來處理這些競爭,同時,還有可能出現(xiàn)一些例如死鎖之類的情況,最后,其實(shí)鎖機(jī)制是一種比較粗糙,粒度比較大的機(jī)制,相對于像計(jì)數(shù)器這樣的需求有點(diǎn)兒過于笨重。
實(shí)現(xiàn)原子操作還可以使用當(dāng)前的處理器基本都支持CAS的指令,只不過每個廠家所實(shí)現(xiàn)的算法并不一樣,每一個CAS操作過程都包含三個運(yùn)算符:一個內(nèi)存地址V,一個期望的值A(chǔ)和一個新值B,操作的時候如果這個地址上存放的值等于這個期望的值A(chǔ),則將地址上的值賦為新值B,否則不做任何操作。
CAS的基本思路就是,如果這個地址上的值和期望的值相等,則給其賦予新值,否則不做任何事兒,但是要返回原值是多少。循環(huán)CAS就是在一個循環(huán)里不斷的做cas操作,直到成功為止。
CAS是怎么實(shí)現(xiàn)線程的安全呢?語言層面不做處理,我們將其交給硬件—CPU和內(nèi)存,利用CPU的多處理能力,實(shí)現(xiàn)硬件層面的阻塞,再加上volatile變量的特性即可實(shí)現(xiàn)基于原子操作的線程安全。
CPU指令對CAS的支持
或許我們可能會有這樣的疑問,假設(shè)存在多個線程執(zhí)行CAS操作并且CAS的步驟很多,有沒有可能在判斷V和E相同后,正要賦值時,切換了線程,更改了值。造成了數(shù)據(jù)不一致呢?答案是否定的。
因?yàn)镃AS是一種系統(tǒng)原語,原語屬于操作系統(tǒng)用語范疇,是由若干條指令組成的,用于完成某個功能的一個過程,并且原語的執(zhí)行必須是連續(xù)的,在執(zhí)行過程中不允許被中斷,也就是說CAS是一條CPU的原子指令,不會造成所謂的數(shù)據(jù)不一致問題。
悲觀鎖,樂觀鎖
說到CAS,不得不提到兩個專業(yè)詞語:悲觀鎖,樂觀鎖。我們先來看看什么是悲觀鎖,什么是樂觀鎖。
悲觀鎖顧名思義,就是比較悲觀的鎖,總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時候都認(rèn)為別人會修改,所以每次在拿數(shù)據(jù)的時候都會上鎖,這樣別人想拿這個數(shù)據(jù)就會阻塞直到它拿到鎖(共享資源每次只給一個線程使用,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程)。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中synchronized和ReentrantLock等獨(dú)占鎖就是悲觀鎖思想的實(shí)現(xiàn)。
而樂觀鎖反之,總是假設(shè)最好的情況,每次去拿數(shù)據(jù)的時候都認(rèn)為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數(shù)據(jù),可以使用版本號機(jī)制和CAS算法實(shí)現(xiàn)。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫提供的類似于write_condition機(jī)制,其實(shí)都是提供的樂觀鎖。我們今天講的CAS就是樂觀鎖。
由于CAS操作屬于樂觀派,它總認(rèn)為自己可以成功完成操作,當(dāng)多個線程同時使用CAS操作一個變量時,只有一個會勝出,并成功更新,其余均會失敗,但失敗的線程并不會被掛起,僅是被告知失敗,并且允許再次嘗試,當(dāng)然也允許失敗的線程放棄操作。基于這樣的原理,CAS操作即使沒有鎖,同樣知道其他線程對共享資源操作影響,并執(zhí)行相應(yīng)的處理措施。同時從這點(diǎn)也可以看出,由于無鎖操作中沒有鎖的存在,因此不可能出現(xiàn)死鎖的情況,也就是說無鎖操作天生免疫死鎖。
CAS 的優(yōu)點(diǎn)
非阻塞的輕量級樂觀鎖, 通過CPU指令實(shí)現(xiàn), 在資源競爭不激烈的情況下性能高, 相比synchronize重量級悲觀鎖, synchronize有復(fù)雜的加鎖, 解鎖和喚醒線程操作。
北京校區(qū)