1. 程式人生 > 其它 >video 的使用(video.min.js)

video 的使用(video.min.js)

CAS是什麼

​ CAS是compare and swap的縮寫中文翻譯為比較並替換

​ 我們都知道,在java語言之前,併發就已經廣泛存在並在伺服器領域得到了大量的應用。所以硬體廠商老早就在晶片中加入了大量直至併發操作的原語,從而在硬體層面提升效率。在intel的CPU中,使用cmpxchg指令。

​ 在Java發展初期,java語言是不能夠利用硬體提供的這些便利來提升系統的效能的。而隨著java不斷的發展,Java本地方法(JNI)的出現,使得java程式越過JVM直接呼叫本地方法提供了一種便捷的方式,因而java在併發的手段上也多了起來。而在Doug Lea提供的cucurenct包中,CAS理論是它實現整個java包的基石。

​ CAS 操作包含三個運算元 —— 記憶體位置(V)、預期原值(A)和新值(B)。 如果記憶體位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新為新值 。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該 位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前 值。)CAS 有效地說明了“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”

​ 通常將 CAS 用於同步的方式是從地址 V 讀取值 A,執行多步計算來獲得新 值 B,然後使用 CAS 將 V 的值從 A 改為 B。如果 V 處的值尚未同時更改,則 CAS 操作成功。

​ 類似於 CAS 的指令允許演算法執行讀-修改-寫操作,而無需害怕其他執行緒同時 修改變數,因為如果其他執行緒修改變數,那麼 CAS 會檢測它(並失敗),演算法 可以對該操作重新計算。

CAS的缺點

  1. 迴圈時間長資源佔用問題,如果替換失敗,執行緒會自旋,直到替換為止,會佔用CPU資源
  2. ABA問題
  3. 只能保證一個共享變數的原子操作

資源佔用問題

//Unsafe.class
public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

​ 例如:getAndAddInt方法內部使用CAS,通過原始碼可知如果當前執行緒沒有比較並替換成功,當前執行緒會迴圈執行下去直到替換成功,假如該執行緒一直未替換成功,那麼此執行緒會一直執行下去,會佔用CPU排程資源。

ABA問題

​ 因為CAS需要在操作值的時候檢查下主記憶體的值是否發生變化,如果沒有發生變化就交換,但是假如一個值原先是A,變成了B,又變成了A,那麼使用CAS進行檢查的時候是未發現此值的變化的,但實際上它是變化的了。ABA問題的解決思路就是使用版本號。在變數前面追加上版本號,每次變數更新的時候把版本號加一,那麼A-B-A 就會變成1A-2B-3A。

如何避免ABA問題

從Java1.5開始JDK的atomic包裡提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等於預期引用,並且當前標誌是否等於預期標誌,如果全部相等,則以原子方式將該引用和該標誌的值設定為給定的更新值。

public class CasDemo {
    public static void main(String[] args) {
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
        //執行緒t1,負責完成ABA的執行
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "當前stamp:" + atomicStampedReference.getStamp());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicStampedReference.compareAndSet(atomicStampedReference.getReference(),atomicStampedReference.getReference() + 1
                    ,atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
            atomicStampedReference.compareAndSet(atomicStampedReference.getReference(),100
                    ,atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
        },"t1").start();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "當前stamp:" + atomicStampedReference.getStamp());
            int stamp = atomicStampedReference.getStamp();
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean result = atomicStampedReference.compareAndSet(atomicStampedReference.getReference(),atomicStampedReference.getReference() + 1
                    ,stamp, stamp + 1);
            System.out.println(Thread.currentThread().getName() + "執行結果:" + result);
        },"t2").start();
        while (Thread.activeCount() > 2) {

        }
    }
}