6.1 JUC — 非阻塞同步機制CAS

CAS的作用

在多線程併發下,可以通過加鎖來保證線程安全性,但多個線程同時請求鎖,而線程的掛起和恢復會有很大的開銷。一些細粒度的操作,例如同步容器,操作往往只有很少代碼量,如果存在鎖並且線程激烈地競爭,調度的代價很大。在硬件的支持下,出現了非阻塞的同步機制,其中一種常用實現就是CAS。

CAS說明

CAS(compare and swap,比較並交換)操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值V與預期原值A相匹配,那麼處理器會自動將該位置值更新爲新值B。否則,處理器不做任何操作並返回V值。當多個線程嘗試使用CAS同時更新一個變量,最終只有一個線程會成功,其他線程都會失敗。但和使用鎖不同,失敗的線程不會被阻塞,而是被告之本次更新操作失敗了,線程可以根據實際情況,繼續重試或者跳過操作,大大減少因爲阻塞而損失的性能。

Atomic原子類

Atomic原子類是CAS操作的一種體現,它分爲基本數據類型原子類、數組類型原子類、引用類型原子類、字段類型原子類,其內部原理都差不多一致。例如AtomicInteger、AtomicReference、AtomicIntegerArray等等。下面以AtomicInteger爲例進行簡單說明。

public class MyThread implements Runnable{
    private AtomicInteger count1 = new AtomicInteger(0);
    private int count2 = 0;

    @Override
    public void run() {
        for (int i = 0; i < 100000; i++) {
            count1.incrementAndGet();
            count2++;
        }
    }

    public void printCount1() {
        System.out.println("count1: " + count1.get());
    }

    public void printCount2() {
        System.out.println("count2: " + count2);
    }
}

public static void main(String[] args){
        MyThread thread = new MyThread();
        Thread t1 = new Thread(thread, "thread1");
        Thread t2 = new Thread(thread, "thread2");
        t1.start();
        t2.start();

        while(Thread.activeCount()>2) {
            Thread.yield();
        }
        thread.printCount1();
        thread.printCount2();
    }

main函數中下面的循環是保證上面線程執行完成,然後輸出結果。count1 是AtomicInteger ,而count2 是正常的int型。用兩個線程一起執行20萬次自增,得到的結果如下:

count1: 200000
count2: 168942

在每次執行的時候,count2的最終結果都不一致,而count1卻始終是正確的結果。說明了AtomicInteger 是線程安全的。

CAS的侷限性

  1. ABA問題
    根據CAS工作的基本原理,存在以下情況,變量初始值爲 100,主線程通過 CAS讀到了 100,接着來一個線程將這個變量改爲 999,之後又一個線程又改成 100 。而輪到主線程發現 a 的值依然是 100,它視作沒有人競爭過,於是修改 a 的值。這種情況,雖然 CAS 會更新成功,但是會存在潛在的問題,中途加入的線程的操作對於後一個線程根本是不可見的。而一般的解決辦法是爲每一次操作加上加時間戳,CAS 不僅關注變量的原始值,還關注上一次修改時間。
  2. 循環時間的額外開銷
    CAS 方法一般都定義在一個循環裏面,直到修改成功纔會退出循環,如果在某些併發量較大的情況下,變量的值始終被別的線程修改,本線程始終在循環裏做判斷比較舊值,效率低下。所以說,CAS 適用於併發量不是很高的場景。
  3. 只能保證一個變量的原子操作
    CAS 只能對一個變量進行原子性操作,而鎖機制則不同,獲得鎖之後,就可以對所有的共享變量進行修改而不會發生任何問題,因爲別人沒有鎖不能修改這些共享變量。

 

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