Android 線程安全(二)CAS

CAS

java中CAS (Compare-and-Swap),比較替換,利用交換指令CMPXCHG來實現,能夠保證操作的原子性

public final boolean compareAndSet(int expect, int update)
參數 (舊值,新值)
舊值用於校驗是否是期待的值,是就能替換成功,不是則失敗,可以繼續重試來更新。java中自旋鎖就是通過循環替換markwork指向來實現的,,線程不會掛起,提高了效率

private int counts = 0;
    private AtomicInteger atomicInteger = new AtomicInteger(0);

    private void testCAS() {
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        count();
                        safeCount();
                    }
                }
            });
            threadList.add(thread);
        }

        for (Thread thread : threadList) {
            thread.start();
        }
        for (Thread thread : threadList) {
            try {
                thread.join();  // 掛起測試線程 ,測試線程等所有計數線程執行完畢纔會輸出結果
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        Log.i("qinxue", "counts: " + counts);
        Log.i("qinxue", "atomicInteger: " + atomicInteger.get());
    }

    private void count() {
        counts++;
    }

    private void safeCount() {
        int oldValue = atomicInteger.get();	//原子操作,可看源碼 volatile 的。
        for (; ; ) {
            boolean r = atomicInteger.compareAndSet(oldValue, ++oldValue);	//循環嘗試更新
            if (r) {
                break;
            }
        }
    }

結果

counts: 9633822
atomicInteger: 10000000

counts 由於++不是原子性操作,count也沒有volatile修飾,各個線程修改後同步到內存,有出現錯誤。例如2個線程緩存了主存的100,都變成101同步到主存,本來主存應該是102的,現在就出現了錯誤,正如結果小於10000000
atomicInteger: 1volatile修飾,讀取、寫入值時是安全的, 2、CAS是原子的,如果在讀取到CAS這時間裏,別的線程CAS了,本線程的CAS會失敗。重試就好了,保證了數據的正確性。
問題
1、ABA問題,例如其他線程修改了1變爲2又變回1,本線程CAS沒有察覺到起始此值已經變化過了,JDK的AtomicStampedReference解決了這個問題,給值添加了版本號。保證不出現ABA。
2、性能消耗,線程不掛起,無效自旋肯定消耗性能的。
3、只能保證一個變量原子性,不如加鎖自由。

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