CAS(Compare And Swap)比較和替換經常使用於設計併發算法.一般的作法是將預期值和一個確切的變量相比較,若是相等,則將變量替換爲新的值.CAS並無想象中的複雜,只要你理解了其實特別的簡單.html
在併發算法和程序中「先檢查後執行」是一個很常見的模式.即先檢查一個變量值而後基於這個變量值執行.以下所示:java
class MyLock {
private boolean locked = false;
public boolean lock() {
if(!locked) {
locked = true;
return true;
}
return false;
}
}
複製代碼
這段代碼在併發環境中會有線程安全問題,在這裏咱們先忽略這點.算法
咱們能夠看到,在lock()方法中,首先去檢查變量locked是否爲false(先檢查),若是是的話則將它設置爲true(後執行).安全
若是多個線程同時調用同個MyLock實例,上面的lock()方法將不能保證是線程安全的.若是線程A此時檢查變量locked發現它爲false,同時線程B也可以對locked進行檢查.事實上只要線程A在沒有設置locked變量爲false以前,其餘線程都能對locked變量進行檢查.因此線程A和B都能檢查和發現locked變量爲false,而且都能在此之上執行.多線程
爲了確保在多線程應用中是線程安全的,「先檢查後執行」操做必須是原子的.原子的意思是檢查和執行必須在同一個原子代碼塊中執行,而不能分開.任何一個線程在執行原子代碼塊的過程當中都不會受到其餘線程的干擾直到結束爲止.沒有線程能在同一時間執行原子代碼塊.併發
下面將上文說起的實例更改成使用synchronized
關鍵字實現的原子代碼塊.post
class MyLock {
private boolean locked = false;
public synchronized boolean lock() {
if(!locked) {
locked = true;
return true;
}
return false;
}
}
複製代碼
如今lock()方式已是同步的了,因此同一時間只能有一個線程可以在Mylock實例上執行.lock()方法一樣已是原子的了.性能
原子版的lock()方法已是一個活生生的「比較和替換」的例子了.lock()方法先比較變量locked是否爲預期值false,若是是的話,則將變量替換爲true.atom
現代CPU大部分已經內建支持原子的比較和替換操做.在Java5中,你可使用java.util.concurrent.atomic
包中的原子對象來使用CPU中的這些功能.spa
下面示例了上文lock()方法如何使用AtomicBoolean對象.
public static class MyLock {
private AtomicBoolean locked = new AtomicBoolean(false);
public boolean lock() {
return locked.compareAndSet(false, true);
}
}
複製代碼
咱們能夠注意到locked已經不是基礎布爾類型,而是AtomicBoolean類型.這個對象有一個compareAndSet()方法用於比較AtomicBoolean實例值,若實例值等於一個預期值,則將它替換爲新的值.在例子中,咱們將比較locked值是否爲false,若是是則將它替換爲true.
compareAndSet()方法若是成功替換值則返回true,不然返回false.
使用Java5+中所提供原子對象的CAS功能,比起你本身實現的CAS帶來的優點是可以使用CPU中的比較和替換特性.這比本身實現的CAS性能要優越得多.
該系列博文爲筆者複習基礎所著譯文或理解後的產物,複習原文來自Jakob Jenkov所著Java Concurrency and Multithreading Tutorial