中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

java多線程CAS的介紹

發布時間:2021-06-22 13:49:16 來源:億速云 閱讀:153 作者:chen 欄目:大數據

這篇文章主要講解了“java多線程CAS的介紹”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“java多線程CAS的介紹”吧!

1 CAS講解

    在工作中,我們往往需要面對多線程計數的問題,我們第一反應,使用“synchronized”,控制并發。

@Slf4j
public class CASDemo extends Thread{

    private static  int  t = 0;

    @Override
    public void run() {
        increment();
        log.info("-----------{}------------",t);
    }

    private synchronized  static  void increment() {
        t++;
    }

    public static void main(String[] args){
        for (int i = 0 ; i < 100 ; i++){
            CASDemo casDemo = new CASDemo();
            casDemo.start();
        }
    }

    結果如下:

java多線程CAS的介紹

    從上面結果來看符合我們的預期,100個線程執行了1次,最后t為100。但是這是最好的實現機制嗎?我們知道“synchronized”是同步鎖,即使jdk6以后已經對其進行了優化(具體可以見另一篇文章:java多線程基礎知識之synchronized原理分析),只是用來計數,是否太大材小用了。有沒有一種更加優雅的解決方案?

@Slf4j
public class CASDemo extends Thread{

    private static AtomicInteger t = new AtomicInteger(0);

    @Override
    public void run() {
        log.info("-----------{}------------",t.incrementAndGet());
    }


    public static void main(String[] args){
        for (int i = 0 ; i < 100 ; i++){
            CASDemo casDemo = new CASDemo();
            casDemo.start();
        }
    }
}

   結果如下:

java多線程CAS的介紹

    從結果上來看,第一種和第二種結果是一樣的。為啥推薦第一種方法呢?因為第二種使用了無鎖式的“compareAndSwap”即“CAS”,既然“CAS”是無鎖的,那么是怎么樣保證其實線程安全的。

“compareAndSwap”從字面上看,這一過程肯定包含了比較和替換兩個動作,具體步驟如下:

(1)線程從內存中讀取 i 的值,假如此時 i 的值為 0,我們把這個值稱為 k 吧,即此時 k = 0。

(2)令 j = k + 1。

(3)用 k 的值與內存中i的值相比,如果相等,這意味著沒有其他線程修改過 i 的值,我們就把 j(此時為1) 的值寫入內存;如果不相等(意味著i的值被其他線程修改過),我們就不把j的值寫入內存,而是重新跳回步驟 1,繼續這三個操作。具體源碼如下:

java多線程CAS的介紹

java多線程CAS的介紹

同時,整個"CAS"是原子的,對應操作系統的一條硬件操作指令,盡管看似有很多操作在里面,但操作系統能夠保證它是原子執行的。

java多線程CAS的介紹

通過上面的流程講解,我們可以發現其不可能從內存中同時取到相同的K值,并且分別+1然后提交到內存中,從而保證的線程安全。

      但是我們仍然面臨一個問題:誰偷偷更改了我的值。

      舉個例子,當線程A即將要執行第三步的時候,線程 B 把 i 的值加1,之后又馬上把 i 的值減 1,然后,線程 A 執行第三步,這個時候線程 A 是認為并沒有人修改過 i 的值,因為 i 的值并沒有發生改變。而這,就是我們平常說的ABA問題。對于基本類型的值來說,這種把數字改變了在改回原來的值是沒有太大影響的,但如果是對于引用類型的話,就會產生很大的影響了。

     怎么解決這個問題呢?——版本控制(參考樂觀鎖)。

     例如,每次有線程修改了引用的值,就會進行版本的更新,雖然兩個線程持有相同的引用,但他們的版本不同,這樣,我們就可以預防 ABA 問題了。Java 中提供了 AtomicStampedReference 這個類,就可以進行版本控制了。

給個demo.

//構造方法, 傳入引用和戳
public AtomicStampedReference(V initialRef, int initialStamp)
//返回引用
public V getReference()
//返回版本戳
public int getStamp()
//如果當前引用 等于 預期值并且 當前版本戳等于預期版本戳, 將更新新的引用和新的版本戳到內存
public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp)
//如果當前引用 等于 預期引用, 將更新新的版本戳到內存
public boolean attemptStamp(V expectedReference, int newStamp)
//設置當前引用的新引用和版本戳
public void set(V newReference, int newStamp)


 

public static void main(String[] args) {

        String str1 = "aaa";
        String str2 = "bbb";
        AtomicStampedReference<String> reference = new AtomicStampedReference<String>(str1,1);
        reference.compareAndSet(str1,str2,reference.getStamp(),reference.getStamp()+1);
        System.out.println("reference.getReference() = " + reference.getReference());

        boolean b = reference.attemptStamp(str2, reference.getStamp() + 1);
        System.out.println("b: "+b);
        System.out.println("reference.getStamp() = "+reference.getStamp());

        boolean c = reference.weakCompareAndSet(str2,"ccc",4, reference.getStamp()+1);
        System.out.println("reference.getReference() = "+reference.getReference());
        System.out.println("c = " + c);
    }

輸出:
reference.getReference() = bbb
b: true
reference.getStamp() = 3
reference.getReference() = bbb
c = false
c為什么輸出false呢, 因為版本戳不一致啦

2jdk8對CAS的優化

      由于采用這種 CAS 機制是沒有對方法進行加鎖的,所以,所有的線程都可以進入 increment() 這個方法,假如進入這個方法的線程太多,就會出現一個問題:每次有線程要執行第三個步驟的時候,i 的值老是被修改了,所以線程又到回到第一步繼續重頭再來。

     而這就會導致一個問題:由于線程太密集了,太多人想要修改 i 的值了,進而大部分人都會修改不成功,白白著在那里循環消耗資源。

     為了解決這個問題,Java8 引入了一個 cell[] 數組,它的工作機制是這樣的:假如有 5 個線程要對 i 進行自增操作,由于 5 個線程的話,不是很多,起沖突的幾率較小,那就讓他們按照以往正常的那樣,采用 CAS 來自增吧。但是,如果有 100 個線程要對 i 進行自增操作的話,這個時候,沖突就會大大增加,系統就會把這些線程分配到不同的 cell 數組元素去,假如 cell[10] 有 10 個元素吧,且元素的初始化值為 0,那么系統就會把 100 個線程分成 10 組,每一組對 cell 數組其中的一個元素做自增操作,這樣到最后,cell 數組 10 個元素的值都為 10,系統在把這 10 個元素的值進行匯總,進而得到 100,二這,就等價于 100 個線程對 i 進行了 100 次自增操作。

     總之,jdk8對于高并發的情況下,采用了類似減少鎖粒度方法來提高性能。



 

感謝各位的閱讀,以上就是“java多線程CAS的介紹”的內容了,經過本文的學習后,相信大家對java多線程CAS的介紹這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

开平市| 合阳县| 贡觉县| 徐闻县| 年辖:市辖区| 靖州| 孝感市| 合阳县| 道真| 贺州市| 佛冈县| 宣城市| 二手房| 攀枝花市| 宜阳县| 阳高县| 鹿邑县| 高雄市| 南投县| 油尖旺区| 肃宁县| 多伦县| 平远县| 浦县| 永清县| 石柱| 读书| 东丽区| 屯门区| 宁海县| 清河县| 丹凤县| 南丰县| 依安县| 曲周县| 茂名市| 维西| 九江市| 府谷县| 清水河县| 京山县|