1. 程式人生 > >原子變數CAS演算法

原子變數CAS演算法

一、CAS演算法
  1、CAS (Compare-And-Swap) 是一種硬體對併發的支援,針對多處理器操作而設計的處理器中的一種特殊指令,用於管理對共享資料的併發訪問。
  2、CAS 是一種無鎖的非阻塞演算法的實現。
  3、CAS 包含了3 個運算元:
    3.1、需要讀寫的記憶體值V
    3.2、進行比較的值A
    3.3、擬寫入的新值B
    3.4、當且僅當V 的值等於A 時,CAS 通過原子方式用新值B 來更新V 的值,否則不會執行任何操作。

二、原子變數
  1、類的小工具包,支援在單個變數上解除鎖的執行緒安全程式設計。事實上,此包中的類可將volatile 值、欄位和陣列元素的概念擴充套件到那些也提供原子條件更新操作的類。
  2、類AtomicBoolean、AtomicInteger、AtomicLong 和AtomicReference 的例項各自提供對相應型別單個變數的訪問和更新。每個類也為該型別提供適當的實用工具方法。
  3、AtomicIntegerArray、AtomicLongArray 和AtomicReferenceArray 類進一步擴充套件了原子操作,對這些型別的陣列提供了支援。這些類在為其陣列元素提供volatile 訪問語義方面也引人注目,這對於普通陣列來說是不受支援的。
  4、核心方法:boolean compareAndSet(expectedValue, updateValue)
  5、java.util.concurrent.atomic 包下提供了一些原子操作的常用類:
  AtomicBoolean 、AtomicInteger 、AtomicLong 、AtomicReference
  AtomicIntegerArray 、AtomicLongArray
  AtomicMarkableReference
  AtomicReferenceArray
  AtomicStampedReference

看如下程式碼:

public class TestAtomic {
    public static void main(String[] args) {
        MyRunnable2 r = new MyRunnable2();
        for(int i=0;i<10;i++){
            new Thread(r).start();
        }
    }
}

class MyRunnable2 implements Runnable{
    private int serialNumber = 0;
    @Override
    public
void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+getAndAdd()); } private int getAndAdd(){ return this.serialNumber++; } }

執行結果如下:
這裡寫圖片描述

可以看出有時會串資料。

結果分析:
i++操作,實際上分為三個步驟“讀-改-寫”
  int temp = i;
  i = i + 1;
  i = temp;
原子變數:在 java.util.concurrent.atomic 包下提供了一些原子變數。
  1. volatile 保證記憶體可見性
  2. CAS(Compare-And-Swap) 演算法保證資料變數的原子性
  CAS 演算法是硬體對於併發操作的支援
  CAS 包含了三個運算元:
    ①記憶體值 V
    ②預估值 A
    ③更新值 B
  當且僅當 V == A 時, V = B; 否則,不會執行任何操作。

與原始碼相比程式碼修改如下:

class MyRunnable2 implements Runnable{
    private AtomicInteger serialNumber = new AtomicInteger(0);
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+getAndAdd());
    }

    private int getAndAdd(){
        return serialNumber.getAndIncrement();
    }
}

其他地方不變。