無鎖算法——CAS原理

一、無鎖算法

CAS(比較與交換,Compare and swap) 是一種有名的無鎖算法。無鎖編程,即不使用鎖的情況下實現(xiàn)多線程之間的變量同步,也就是在沒有線程被阻塞的情況下實現(xiàn)變量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。實現(xiàn)非阻塞同步的方案稱為“無鎖編程算法”( Non-blocking algorithm)。?

相對應(yīng)的,獨占鎖是一種悲觀鎖,synchronized就是一種獨占鎖,它假設(shè)最壞的情況,并且只有在確保其它線程不會造成干擾的情況下執(zhí)行,會導(dǎo)致其它所有需要鎖的線程掛起,等待持有鎖的線程釋放鎖。?

使用lock實現(xiàn)線程同步有很多缺點:?

* 產(chǎn)生競爭時,線程被阻塞等待,無法做到線程實時響應(yīng)。?

* dead lock,死鎖。?

* live lock。?

* 優(yōu)先級翻轉(zhuǎn)。?

* 使用不當,造成性能下降。?

當然在部分情況下,目前來看,無鎖編程并不能替代 lock。

二、實現(xiàn)級別

非同步阻塞的實現(xiàn)可以分成三個級別:wait-free/lock-free/obstruction-free。?

wait-free?

是最理想的模式,整個操作保證每個線程在有限步驟下完成。?

保證系統(tǒng)級吞吐(system-wide throughput)以及無線程饑餓。?

截止2011年,沒有多少具體的實現(xiàn)。即使實現(xiàn)了,也需要依賴于具體CPU。?

lock-free?

允許個別線程饑餓,但保證系統(tǒng)級吞吐。確保至少有一個線程能夠繼續(xù)執(zhí)行。?

wait-free的算法必定也是lock-free的。?

obstruction-free?

在任何時間點,一個線程被隔離為一個事務(wù)進行執(zhí)行(其他線程suspended),并且在有限步驟內(nèi)完成。在執(zhí)行過程中,一旦發(fā)現(xiàn)數(shù)據(jù)被修改(采用時間戳、版本號),則回滾。也叫做樂觀鎖,即樂觀并發(fā)控制(OOC)。事務(wù)的過程是:1讀取,并寫時間戳;2準備寫入,版本校驗;3校驗通過則寫入,校驗不通過,則回滾。?

lock-free必定是obstruction-free的。

三、CAS算法

CAS(比較與交換,Compare and swap) 是一種有名的無鎖算法。CAS, CPU指令,在大多數(shù)處理器架構(gòu),包括IA32、Space中采用的都是CAS指令,CAS的語義是“我認為V的值應(yīng)該為A,如果是,那么將V的值更新為B,否則不修改并告訴V的值實際為多少”,CAS是項 樂觀鎖 技術(shù),當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。CAS有3個操作數(shù),內(nèi)存值V,舊的預(yù)期值A(chǔ),要修改的新值B。當且僅當預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值V修改為B,否則什么都不做。?

java.util.concurrent.atomic中的AtomicXXX,都使用了這些底層的JVM支持為數(shù)字類型的引用類型提供一種高效的CAS操作,而在java.util.concurrent中的大多數(shù)類在實現(xiàn)時都直接或間接的使用了這些原子變量類,這些原子變量都調(diào)用了 sun.misc.Unsafe 類庫里面的 CAS算法,用CPU指令來實現(xiàn)無鎖自增,JDK源碼:

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);?

}

因而在大部分情況下,java中使用Atomic包中的incrementAndGet的性能比用synchronized高出幾倍。

四、ABA問題

thread1意圖對val=1進行操作變成2,cas(val,1,2)。?

thread1先讀取val=1;thread1被搶占(preempted),讓thread2運行。?

thread2 修改val=3,又修改回1。?

thread1繼續(xù)執(zhí)行,發(fā)現(xiàn)期望值與“原值”(其實被修改過了)相同,完成CAS操作。?

使用CAS會造成ABA問題,特別是在使用指針操作一些并發(fā)數(shù)據(jù)結(jié)構(gòu)時。?

解決方案?

ABA?:添加額外的標記用來指示是否被修改。?

從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等于預(yù)期引用,并且當前標志是否等于預(yù)期標志,如果全部相等,則以原子方式將該引用和該標志的值設(shè)置為給定的更新值。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 鎖的代價 鎖是用來做并發(fā)最簡單的方式,當然其代價也是最高的。內(nèi)核態(tài)的鎖在鎖的時候需要操作系統(tǒng)進行一次上下文切換,加...
    cgw丶閱讀 3,309評論 0 5
  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,918評論 0 11
  • 不講語言特性,只從工程角度出發(fā),個人覺得C++標準委員會在C++11中對多線程庫的引入是有史以來做得最人道的一件事...
    stidio閱讀 14,009評論 0 11
  • 郵箱里客戶詢盤堆積成山 內(nèi)容良莠不齊 潛在目標客戶? 竊取行業(yè)信息? 高質(zhì)量合作客戶? 同行惡意競爭? 業(yè)務(wù)員早已...
    貿(mào)立方閱讀 388評論 0 0
  • Stack Overflow 的數(shù)據(jù)科學(xué)家 David Robinson 發(fā)現(xiàn),軟件行業(yè)的分工讓不同發(fā)達地區(qū)的程序...
    老牛哥兒閱讀 544評論 0 0

友情鏈接更多精彩內(nèi)容