更新時間:2021-01-04 08:43:36 來源:動力節點 瀏覽1623次
CAS(compare and swap)是解決多線程并行情況下使用鎖造成性能損耗的一種機制。多線程CAS操作包含3個操作數,分別是內存位置V、預期原值A和新值B。如果內存位置的值與預期原值相匹配,那么處理器會自動將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會在CAS指令之前返回該位置的值。多線程CAS有效地說明了“我認為位置V應該包含值A;如果包含該值,則將B放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。
這些處理過程有指令集的支持,因此看似讀-寫-改操作只是一個原子操作,所以不存在線程安全問題。我們看個cas的操作過程偽代碼:
int value ;
int compareAndSwap (int oldValue,int newValue){
int old_reg_value =value ;
if (old_reg_value==old_reg_value)
value =newValue;
return old_reg_value ;
}
當多個線程嘗試使用CAS同時更新同一個變量的時候,只有其中一個線程能夠更新變量的值。當其他線程失敗后,不會像獲取鎖一樣被掛起,而是可以再次嘗試,或者不進行任何操作,這種靈活性就大大減少了鎖活躍性風險。
CAS的特性決定了CAS的功能和作用,我們知道采用鎖對共享數據進行處理的話,當多個線程競爭的時候,都需要進行加鎖,沒有拿到鎖的線程會被阻塞,以及喚醒,這些都需要用戶態到核心態的轉換,這個代價對阻塞線程來說代價還是蠻高的,那cas是采用無鎖樂觀方式進行競爭,性能上要比鎖更高些才是,為何不對鎖競爭方式進行替換?
要回答這個問題,我們先舉個例子。當你開車在上班高峰期的時候,如果通過交通信號燈來控制車流,可以實現更高的吞吐量,而環島雖然無紅綠燈讓你等待,但你一圈不一定能繞出你先出去的那個路口,有時候可能得多走幾圈,而在低擁堵的時候,環島則能實現更高的吞吐量,你一次就可以成功,而紅路燈反而效率低下了,即便人不多,你依然需要等待。
這個例子依然適應鎖和cas的比較,在高度競爭的情況下,鎖的性能將超過cas的性能,但在中低程度的競爭情況下,cas性能將超過鎖的性能。多數情況下,資源競爭一般都不會那么激烈。
我們參考一個ConcurrentLinkedQueue 的源碼實現,來看下cas的應用。
ConcurrentLinkedQueue是一個基于鏈接節點的無界線程安全隊列,它是個單向鏈表,每個鏈接節點都擁有一個當前節點的元素和下一個節點的指針。
Node< E> {
volatile E item;
volatile Node< E> next;
}
它采用先進先出的規則對節點進行排序,當我們添加一個元素的時候,它會添加到隊列的尾部(tail),當我們獲取一個元素時,它會返回隊列頭部(head)的元素。tail節點和head節點方便我們快速定位最后一個和第一個元素。
在有兩次CAS操作的情況下,如何保證一致性呢?具體分為三種情況如下:
1、如果第一個cas更新成功,第二個失敗,那么對了tail會出現不一致的情況。而且即便是都更新成功了,在執行兩個cas之間,仍然可能有另外一個線程會訪問這個隊列,那么如何保證這種多線程情況下不會出錯。
2、對于第一個問題,即便tail更新失敗,上述代碼也會循環的找到真正的尾節點,在這里不是強制要求以tail為尾節點,它只是一個靠近尾節點的指針。
3、第二種情況,如果線程B抵達時候,發現線程A正在執行更新,那么B線程會通過反復循環來檢查隊列的狀態,直到A完成更新,B線程又拿到了nextNode最新信息,添加新的node,從而使兩個線程不會相互干擾。
講了那么多,還是離不開CAS解決多線程并行情況下使用鎖造成性能損耗的本質,萬變不離其宗,只要我們掌握了這一點學起多線程CAS也就事半功倍了。在本站的多線程教程中還有更多的多線程的各種奇特的機制的詳細解析,幫助我們更好地學習多線程知識。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習