1. 程式人生 > >Java CAS(Compare And Swap)的一點理解

Java CAS(Compare And Swap)的一點理解

CAS: Compare And Swap, 即比較和替換。

思想: CAS的比較、替換操作是非阻塞操作, 它有3個引數分別為記憶體值、預期值和更新值。 當記憶體值和預期值匹配時則更新, 不匹配時直接返回。

PS: CAS比synchronized效率要好,  因為CAS是c語言實現的cpu鎖機制, synchronized是Java鎖。

        簡單的說CAS提供了一種實現原子操作的方法, 都封裝在sun.misc.Unsafe類裡。 而在實際開發過程中, 並不建議直接操作Unsafe類, 而且使用基於Unsafe類實現的各個派生類。


     這些類都提供了原子操作, 假設使用AtomicInteger模擬生產者消費者模式。 

 AtomicInteger object = new AtomicInteger(0);
        for (int i = 0; i < 4; i++) {
            //邏輯上的生產者, 值從0到1
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(100);
                        } catch (Exception ex) {
                        }
                        System.out.println("判斷0執行緒,當前值是"+ object.get()
                                + ", 返回值"+ object.compareAndSet(0, 1));   //compare函式是非阻塞
                    }
                }
            }.start();

            //邏輯上的消費者, 值從1到0
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(100);
                        } catch (Exception ex) {
                        }
                        System.out.println("判斷1執行緒,當前值是" + object.get()
                                + ", 返回值" +  object.compareAndSet(1, 0));  //非阻塞
                    }
                }
            }.start();
        }
     4個生產者執行緒、4個開發者執行緒。 生產者執行緒的作用是0到1, 消費者執行緒的作用是1到0。 程式碼裡並沒有同步控制邏輯(即沒有synchronized/Lock), 執行程式看看效果。
判斷0執行緒,當前值是0, 返回值true
判斷0執行緒,當前值是0, 返回值true
判斷1執行緒,當前值是0, 返回值false
判斷1執行緒,當前值是1, 返回值true
判斷0執行緒,當前值是0, 返回值true
判斷1執行緒,當前值是1, 返回值true
判斷1執行緒,當前值是1, 返回值true
判斷0執行緒,當前值是1, 返回值false
判斷0執行緒,當前值是0, 返回值true
判斷0執行緒,當前值是1, 返回值false
判斷1執行緒,當前值是1, 返回值true
判斷1執行緒,當前值是0, 返回值false
判斷0執行緒,當前值是0, 返回值true
判斷1執行緒,當前值是1, 返回值true
        程式執行正常, 並沒有報共享衝突錯誤。
原理:

1、 volatile關鍵字保證這8個執行緒操作的是同一塊記憶體;

2、 原子性操作compareAndSet即比較並更新, 是在native層實現的, 原理見下面的2篇部落格。


CAS裡所有ABA問題其實就是值在改回來前不知道發生了什麼,  例如: 你讀取的值是1, 但前面可能經歷了3、2等值的情況。 

使用AtomicStampedReference可以解決ABA問題, 因為每次更新值時都有一個時間戳。 在compareAndSet時既要比較值,也要比較時間戳是否一致。


    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }

參考部落格:

https://www.jianshu.com/p/24ffe531e9ee

http://zl198751.iteye.com/blog/1848575