CAS是什麼?Atomic包知多少?

JDK1.5中增加的一個最主要的支持是Atomic類,如AtomicInteger、AtomicLong等,這些類可幫助最大限度地減少在多線程中對於一些基本操作(例如,增加或減少多個線程之間共享的值)的複雜性,而這些類的實現都依賴於CAS(compare and swap)的算法。

一、CAS

1.CAS原理

CAS的全稱是Compare And Swap——比較交換,CAS中有三個核心參數:

  • 主內存中存放的V值,所有線程共享。
  • 線程上次從內存中讀取的V值A存放在線程的幀棧中,每個線程私有。
  • 需要寫入內存中並改寫V值的B值。也就是線程對A值操作後放入到主存V中。

CAS的核心是在將B值寫入到V之前要比較A值和V值是否相同,如果不相同證明此時V值已經被其他線程改變,重新將V值賦給A,並重新計算得到B,如果相同,則將B值賦給V。

CAS是一種樂觀鎖,當多個線程同時使用CAS操作一個變量時,只有一個會勝出,併成功更新,其餘均會失敗,但失敗的線程並不會被掛起,僅是被告知失敗,並且允許再次嘗試,直到成功爲止(JDK代碼中採用while循環不斷自旋)。由於無鎖,因此不可能出現死鎖的情況,也就是說無鎖操作天生免疫死鎖。

source: 全面瞭解Java中的CAS機制

2.CAS的優劣

cpu是時分複用的,也就是把cpu的時間片,分配給不同的thread/process輪流執行,時間片與時間片之間,需要進行cpu切換,也就是會發生進程的切換。如果採用悲觀鎖(如Sychronizied獨佔鎖),在進程掛起和恢復執行過程中存在着很大的開銷,同時當一個線程正在等待鎖時,它不能做任何事。用樂觀鎖在讀多寫少的場景下,性能就會比較優越。

CAS之不足:

  • 循環時間長開銷很大:如果CAS失敗,會一直進行嘗試。如果CAS長時間一直不成功,可能會給CPU帶來很大的開銷。

  • 只能保證一個共享變量的原子操作:當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖來保證原子性。

  • ABA問題
    如果一開始位置V得到的舊值是A,當進行賦值操作時再次讀取發現仍然是A,並不能說明變量沒有被其它線程改變過。有可能是其它線程將變量改爲了B,後來又改回了A。大部分情況下ABA問題不會影響程序併發的正確性,如果要解決ABA問題,可以用傳統的互斥同步。Java併發包爲了解決這個問題,提供了一個帶有標記的原子引用類AtomicStampedReference,它可以通過控制變量值的版本來保證CAS的正確性。

source: 面試必問的CAS,你懂了嗎

二、Atomic原子包

原子更新基本類型主要包括3個類:

  • AtomicBoolean:原子更新布爾類型
  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新長整型

這3個類的實現原理和使用方式幾乎是一樣的,提供了原子自增方法、原子自減方法以及原子賦值方法等。原子類的內部幾乎是基於Unsafe類中的CAS相關操作的方法實現的,這也同時證明其是基於無鎖實現的。Unsafe類存在於sun.misc包中,其內部方法操作可以像C的指針一樣直接操作內存,因爲Java中CAS操作的執行依賴於Unsafe類的方法,注意Unsafe類中的所有方法都是native修飾的,也就是說Unsafe類中的方法都直接調用操作系統底層資源執行相應任務。

//JDK 1.7的源碼,由for的死循環實現,並且直接在AtomicInteger實現該方法,
//JDK1.8後,該方法實現已移動到Unsafe類中,直接調用getAndAddInt方法即可
public final int incrementAndGet() {
    for (;;) {
        int current = get();
        int next = current + 1;
        if (compareAndSet(current, next))
            return next;
    }

source:JAVA中的CAS

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章