標籤:機器 處理 expected blog pre 效率 expec shu cpu
Java中的樂觀鎖與悲觀鎖;
1. Java中典型的synchronized就是一種悲觀鎖,也就是獨佔鎖,不過JDK1.6之後對synchronized已經做了許多最佳化,也不能說是完全的悲觀鎖了;
2. 樂觀鎖是一種思想,即認為讀多寫少,遇到並發寫的可能性比較低,所以採取在寫的時候先讀出版本號碼,然後比較更新。而CAS(Compare and Swap)即是一種典型的樂觀鎖的實現。需要注意的是,CAS是一種思想,而不是某一項具體的技術。
CAS 操作包含三個運算元 —— 記憶體位置(V)、預期的原值(A)和新值(B)。如果記憶體位置的值V與預期原值A相匹配,那麼處理器會自動將該位置值更新為新值B。CAS是一種更新的原子操作,通俗的講,就是比較當前值和傳入值是否一樣,一樣則更新;
JDK1.5中引入了底層的支援,特別是在java.util.concurrent中的類中有著廣泛的應用。在int、long和對象的引用等類型上都公開了CAS的操作,並且JVM把它們編譯為底層硬體提供的最有效方法,在運行CAS的平台上,運行時把它們編譯為相應的機器指令。在java.util.concurrent.atomic包下面的所有的原子變數類型中,比如AtomicInteger,都使用了這些底層的JVM支援為數字類型的參考型別提供一種高效的CAS操作。
ABA問題:在CAS操作中,會出現ABA問題。
比如說一個線程one從記憶體位置V中取出A,這時候另一個線程two也從記憶體中取出A,並且two進行了一些操作變成了B,然後two又將V位置的資料變成A,這時候線程one進行CAS操作發現記憶體中仍然是A,然後one操作成功。儘管線程one的CAS操作成功,但是不代表這個過程就是沒有問題的。這就是所謂的ABA問題。
ABA的簡單的解決方案就是增加一個版本號碼,更新的時候更新引用和版本號碼的值。AtomicStampedReference和AtomicMarkableReference兩個類都可以解決ABA問題。AtomicStampedReference更新的時候,先檢查當前引用是否等於預期引用,再檢查當前標識是否等於預期標識,相當於間接的給引用加上了“版本號碼”,從而避免ABA問題,AtomicMarkableReference將更新一個“對象引用-布爾值”的二元組。
AtomicStampedReference的compareAndSet方法原始碼如下:
/** * Atomically sets the value of both the reference and stamp * to the given update values if the * current reference is {@code ==} to the expected reference * and the current stamp is equal to the expected stamp. * * @param expectedReference the expected value of the reference * @param newReference the new value for the reference * @param expectedStamp the expected value of the stamp * @param newStamp the new value for the stamp * @return {@code true} if successful */ 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))); }
CAS與Synchronized的使用情景:
1、對於資源競爭較少(線程衝突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及使用者態核心態間的切換操作額外浪費消耗cpu資源;而CAS基於硬體實現,不需要進入核心,不需要切換線程,操作自旋幾率較少,因此可以獲得更高的效能。
2、對於資源競爭嚴重(線程衝突嚴重)的情況,CAS自旋的機率會比較大,從而浪費更多的CPU資源,效率低於synchronized。
補充: synchronized在jdk1.6之後,已經改進最佳化。synchronized的底層實現主要依靠Lock-Free的隊列,基本思路是自旋後阻塞,競爭切換後繼續競爭鎖,稍微犧牲了公平性,但獲得了高輸送量。線上程衝突較少的情況下,可以獲得和CAS類似的效能;而線程衝突嚴重的情況下,效能遠高於CAS。
參考:http://www.jianshu.com/p/59ddb7002b30
https://www.cnblogs.com/qjjazry/p/6581568.html
Java-悲觀鎖和樂觀鎖