Java多線程並發中 CAS 的使用與理解


一、CAS (Compare And Swap):

  CAS(Compare And Swap),即比較並交換 CAS(V,E,N)。是解決多線程並行情況下使用鎖造成性能損耗的一種機制,CAS操作包含三個操作數——要更新的變量(V)、預期原值(E)和新值(N)核心算法是如果V 值等於E 值,則將V 的值設為N 。若V 值和E 不同,則說明已經有其他線程做了更新,則當前線程不做更新,直到V、E兩個值相等,才更新V的值。
  1、代碼演示:

/*
* 原子變量類:
*      AtomicBoolean
*      AtomicInteger
*      AtomicLong
*      AtomicReference
*
* 原子數組類:
*      AtomicLongArray
*      AtomicReferenceArray
*
* 原子方式更新對象中的字段類:
*      AtomicIntegerFieldUpdate
*      AtomicReferenceFieldUpdate
*
* 高並發匯總類:
*      LongAdder
*      LongAccumulator
*      DoubleAdder
*      DoubleAccumulator
*
*/
public class CASTest {

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger counter = new AtomicInteger(0);
        CountDownLatch c = new CountDownLatch(10000);

        for(int i = 0; i < 50; i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.print( counter.incrementAndGet() + " ");            
                    c.countDown();
                }
            }).start();
        }
        c.await();
        System.out.println("AtomicInteger :" +  counter.get());
    }
}

 

二、CAS 中 ABA 問題:

  ABA 問題是指假設當前值為 A ,如果另一個線程先將 A 修改成 B , 再修改回成 A ,當前線程的 CAS 操作無法分辨當前值發生過變化。ABA 是不是一個問題與程序的邏輯有關,一般不是問題。而如果確實有問題,解決方法是使用 AtomicStampedReference ,在修改值的同時附加一個時間戳,只有值和時間戳都相同才進行修改。

  1、代碼演示:

public class ABATest {
public static void main(String[] args) throws InterruptedException { CASABATest(); atomicStampedReferenceTest(); } public static void atomicStampedReferenceTest(){ AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(0, 0); // 第一個參數:引用對象的初始值,第二個參數:這個對象的初始標記 new Thread(new Runnable() { @Override public void run() {
         // compareAndSet(eR,nR,eS,nS) eR:引用對象的期望值,nR:引用對象的新值,eS:引用對象期望標記,nS:引用對象的新標記,當 eR、eS和當前線程的值相等才會修改eR、eS的值為nR、nS System.out.println(Thread.currentThread().getName()
+ ", " + atomicStampedReference.compareAndSet(0, 1, 0, 1) + ", atomicStampedReference value:" + atomicStampedReference.getReference()); } },"Thread A").start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(1, 0, 1, 2) + ", atomicStampedReference value:" + atomicStampedReference.getReference()); } },"Thread B").start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ", " + atomicStampedReference.compareAndSet(0, 1, 0, 1) + ", atomicStampedReference value:" + atomicStampedReference.getReference()); } },"Thread C").start(); } /** * 演示CAS 更新的ABA 問題 * */ public static void CASABATest() throws InterruptedException { AtomicInteger integer = new AtomicInteger(0); new Thread(new Runnable() { @Override public void run() {
          //compareAndSet(e,u) e:期望值,u:新值,如果e的值在當前線程中和integer的值一致,就把e的值修改成u的值 System.out.println(Thread.currentThread().getName()
+ ", " + integer.compareAndSet(0, 1) + ", AtomicInteger value:" + integer); //true } },"Thread A").start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(1 , 0) + ", AtomicInteger value:" + integer); //true } },"Thread B").start(); new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName() + ", " + integer.compareAndSet(0 , 1) + ", AtomicInteger value:" + integer); //true } },"Thread C").start(); } }

 

三、CAS 與 Synchronized 的對比:
  1.synchronized 是悲觀的,它假設更新都是可能沖突的,所以要先獲取鎖,得到鎖才更新,它是阻塞式算法,得不到鎖就進入鎖池等待。
  CAS 是樂觀的,它假設沖突比較少,但使用CAS 更新,進行沖突檢測,如果確實沖突就繼續嘗試直到成功,它是非阻塞式算法,有更新沖突就重試。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM