老是假設最壞的狀況,每次獲取數據都認爲別人會修改,因此拿數據時會上鎖,一直到釋放鎖不容許其餘線程修改數據。Java中如synchronized和reentrantLock就是這種實現。html
老是假設最好的狀況,每次去拿數據時都認爲別人不會修改,因此不上鎖,等更新數據時判斷一下在此期間是否有其餘人更新過這個數據,可使用CAS算法實現。樂觀鎖適用於多讀少寫的應用類型,能夠大幅度提升吞吐量。樂觀鎖的實現機制主要包括版本號機制(給數據加一個版本號,數據被修改版本號會加一,更新時讀取版本號,若讀取到的版本號和以前一致才更新,不然駁回)和CAS算法(下詳)。java
多線程互斥訪問時會進入鎖機制。互斥設計時會面臨一個狀況:沒有得到鎖的進程如何處理。一般有兩種辦法:一種是沒有得到鎖就阻塞本身,請求OS調度另外一個線程上的處理器,即互斥鎖;另外一種時沒有得到鎖的調用者就一直循環,直到鎖的持有者釋放鎖,即自旋鎖。面試
自旋鎖是一種較低級的保護數據的方式,存在兩個問題:遞歸死鎖,即遞歸調用時試圖得到相同的自旋鎖。過多佔用CPU資源,自旋鎖不成功時會持續嘗試,一般一個自旋鎖會有參數限制嘗試次數,超出後放棄time slice,等待一下一輪機會。算法
但在鎖持有者保持鎖的時間較短的前提下,選擇自旋而非睡眠則大大提升了效率,於是在這種狀況下自旋鎖效率遠高於互斥鎖。編程
CAS即compare and swap,是一種系統原語,是不可分割的操做系統指令。CAS是一種樂觀鎖實現。數組
CAS有三個操做數,內存值V,舊的預期內存值A,要修改的新值B,當且僅當A=V,纔將內存值V修改成B,不然不會執行任何操做。通常狀況下CAS是一個自旋操做,即不斷重試。緩存
CAS是CPU指令集的操做,只有一步的原子操做,因此很是快,CAS的開銷主要在於cache miss問題。如圖多線程
這是一個8核CPU系統,共有4個管芯,每一個管芯中有兩個CPU,每一個CPU有cache,管芯內有一個互聯模塊,讓管芯的兩個核能夠互相通訊。圖中的系統鏈接模塊可讓四個管芯通訊。例如,此時CPU0進行一個CAS操做,而該變量所在的緩存線在CPU7的高速緩存中,則流程以下:併發
JDK5增長java.util.concurrent包,其中不少類使用了CAS操做。這些CAS操做基於Unsafe類中的native方法實現:app
//第一個參數o爲給定對象,offset爲對象內存的偏移量,經過這個偏移量迅速定位字段並設置或獲取該字段的值, //expected表示指望值,x表示要設置的值,下面3個方法都經過CAS原子指令執行操做, //設置成功返回true,不然返回false。 public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x); public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x); public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);
因爲CAS做用的對象在主存裏而不是在線程的高速緩存裏,CAS操做在Java中須要配合volatile使用。
Java中的CAS主要包含如下幾個問題:
jdk1.5提供了一組原子類,由CAS對其實現。其中的類能夠分爲四組:
其做用爲對單一數據的操做實現原子化,無需阻塞代碼,但訪問兩個或兩個以上的atomic變量或對單個atomic變量進行2次或2次以上的操做被認爲是須要同步的以便這些操做是一個原子操做。
前四種類型用來處理Boolean,Integer, Long, 對象,後兩個類支持的方法和AtomicReference基本一致,僅做用不一樣。以上類型均包含如下方法:
對於AtomicInteger, AtomicLong,還實現了getAndIncrement(), increateAndGet(), getAndDecreate(), decreateAndGet(), addAndGet(delta), getAndAdd(delta)方法,以實現加減法的原子操做。
這三種類型用於處理數組,經常使用方法以下:
對於AtomicIntegerArray, AtomicLongArray,還實現了getAndIncrement(index), increateAndGet(index), getAndDecreate(index), decreateAndGet(index), addAndGet(index, delta), getAndAdd(index, delta)方法,以實現加減法的原子操做。
這三種類型用於處理普通對象中某個字段的CAS更新,因爲是CAS更新,要求該字段必須是volatile的,經常使用方法以下:
示例操做以下:
User類(由普通類改形成的CAS更新類)
public class User { private static AtomicReferenceFieldUpdater<User, String> nameUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name"); private static AtomicIntegerFieldUpdater<User> ageUpdater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); private volatile String name; private volatile int age; public User(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void lazySetName(String name) { nameUpdater.lazySet(this, name); } public String getSetName(String name) { return nameUpdater.getAndSet(this, name); } public void compareAndSetName(String exceptedName, String newName) { nameUpdater.compareAndSet(this, exceptedName, newName); } public void lazySetAge(int age) { ageUpdater.lazySet(this, age); } public Integer getSetAge(int age) { return ageUpdater.getAndSet(this, age); } public void compareAndSetAge(int exceptedAge, int newAge) { ageUpdater.compareAndSet(this, exceptedAge, newAge); } }
主程序
public class AtomicTest { public void run() { User user = new User("Atomic", 10); user.compareAndSetName("Atomic", "Ass"); user.compareAndSetAge(10, 11); System.out.println(user.getName() + user.getAge()); } public static void main(String[] args) throws Exception { new AtomicTest().run(); } }
輸出結果:
Ass11
深刻理解CAS算法原理
面試必備之樂觀鎖與悲觀鎖
Java之多線程 Atomic(原子的)
對 volatile、compareAndSet、weakCompareAndSet 的一些思考
併發編程面試必備:JUC 中的 Atomic 原子類總結
AtomicReference,AtomicStampedReference與AtomicMarkableReference的區別
JAVA中的CAS