Java多執行緒(5):CAS

2022-10-28 09:01:16

 

您好,我是湘王,這是我的部落格園,歡迎您來,歡迎您再來~

 

 

JDK1.5之前,Java的多執行緒都是靠synchronized來保證同步的,這會引起很多效能問題,例如死鎖。但隨著Java的不斷完善,JNI(Java Native Interface)使得Java能越過JVM直接呼叫本地方法,例如CAS。

CAS是Compare And Swap(比較與交換)的縮寫,它用於實現多執行緒同步的原子指令,允許演演算法執行讀-修改-寫操作,而無需擔心其他執行緒同時修改變數。說人話,意思就是它的操作過程足夠細微,以至於執行緒都奈何不了它。

所謂原子指令就是指不會被執行緒排程機制打斷的操作指令,這種操作一旦開始,就一直執行到結束,中間不會有任何執行緒切換,即要麼全部完成,要麼全部中斷。換一種說法,就是CAS可以保證Java運算實現我們想要的操作而無需擔心會受到多執行緒的影響。

某種程度上,CAS可以用來取代synchronized的強制同步,提升效能。其實整個java.util.concurrent包都是建立在CAS之上的,尤其是Java中大多數鎖的實現基礎類別AbstractQueuedSynchronizer,更是以CAS為基礎,提供了一系列的獨佔鎖、共用鎖、可重入鎖、自旋鎖、讀寫鎖等多執行緒控制手段(這在後面會說)。就像圖中那樣:

 

 

 

Java對CAS的實現都在java.util.concurrent.atomic包下(java.util.concurrent也簡稱JUC,這是個簡稱。所以如果有面試官說想讓你談談JUC相關的問題,不要一臉懵,否則會被立即淘汰)。AtomicInteger為例,從原始碼可以看出CAS操作都是通過sun包下Unsafe類實現,而Unsafe類中的方法都是native方法,由本地實現,和作業系統、CPU都有關係。CAS有一個比較通用的實現模式:

1、首先宣告(共用)變數為volatile

2、然後使用CAS的原子條件來更新

3、同時配合volatile的可見性來實現執行緒之間的同步

前面講過,不用深究volatile關鍵字的用途,因為隨著機器設定的豪華,其實這個關鍵字已經沒啥用了,而且也可以看到,CAS裡面也有大量出現,JDK已經替你用好了,自己如果不太熟悉就不要用了。CAS相關類結構圖是:

 

 

 

還是老規矩,用程式碼來舉例:

/**
 * 僅用AtomicInteger實現CAS
 *
 * @author 湘王
 */
public class AtomicIntegerTester1 {
    // 使用AtomicInteger實現CAS,有沒有volatile都不影響
    public static volatile AtomicInteger atomicInteger = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 20; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    atomicInteger.getAndIncrement();
                }
            };
            executor.submit(runnable);
        }

        // 為了觀察效果休眠,但實際生產環境中肯定不允許
        Thread.sleep(100);
        executor.shutdown();
        System.out.println(atomicInteger.get());
    }
}



/**
 * 用AtomicIntegerFieldUpdater實現CAS
 *
 * @author 湘王
 */
public class AtomicIntegerTester2 {
    /*
     * 使用AtomicIntegerFieldUpdater實現CAS,相關計算欄位必須用volatile修飾,不然拋異常
     * Caused by: java.lang.IllegalArgumentException: Must be volatile type
     *
     */
    public volatile int count = 0;
    public static final AtomicIntegerFieldUpdater<AtomicIntegerTester2> lockUpdate = AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerTester2.class, "count");

    public int increase(int inc) {
        return lockUpdate.addAndGet(this, inc);
    }

    public int get() {
        return lockUpdate.get(this);
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicIntegerTester2 tester = new AtomicIntegerTester2();
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 20; i++) {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    tester.increase(1);
                }
            };
            executor.submit(runnable);
        }
        // 為了觀察效果休眠,但實際生產環境中肯定不允許
        Thread.sleep(100);
        executor.shutdown();
        System.out.println(tester.get());
    }
}

 

 

 

CAS的內容並不多,可以看看它的原始碼,還是比較有意思的。

 


 

 

 

感謝您的大駕光臨!諮詢技術、產品、運營和管理相關問題,請關注後留言。歡迎騷擾,不勝榮幸~