樂觀鎖,總是樂觀地假設最好的情況,每次去拿資料的時候都認為別人不會修改這個資料,所以不會上鎖,只會要對資料進行更新時判斷一下在此期間(拿到資料到更新的期間)別人有沒有去更改這個資料,可以使用版本號機制和CAS演演算法實現。
以AtomicInteger為例看看底層是怎麼進行操作的
AtomicInteger integer=new AtomicInteger(123);
int a=integer.addAndGet(321);//+321
System.out.println(a);//結果為444
上面編寫了一個範例,建立一個AtomicInteger物件,呼叫它的addAndGet方法,此方法是加上一個數並返回相加後的結果。然後我們來看看這個方法的原始碼。
public final int addAndGet(int delta) {
return U.getAndAddInt(this, VALUE, delta) + delta;
}
可以看到這個方法的返回值呼叫了U(Unsafe物件)的getAndInt方法來獲取當前物件在記憶體中的值
下面是getAndInt方法的原始碼
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);//獲取物件中offset偏移地址對應的整型field的值
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
可以看到邏輯就是若weakCompareAndSetInt的返回值為false則不斷的獲取整形值field
下面是weakCompareAndSetInt的原始碼
@HotSpotIntrinsicCandidate
public final boolean weakCompareAndSetInt(Object o, long offset,
int expected,
int x) {
return compareAndSetInt(o, offset, expected, x);//比較當前記憶體中的值和期望值x是否相等
}
總是假設最壞的情況,每次去拿資料的時候都認為別人會修改,所以每次在拿資料的時候都會上鎖,這樣別人想拿這個資料就會阻塞直到它拿到鎖。Java中synchronized和ReentrantLock等獨佔鎖就是悲觀鎖思想的實現。
Java中的關鍵字,是由JVM來維護的。是JVM層面的鎖。
是非公平鎖。
如果獲取鎖的執行緒由於要等待一些原因(比如呼叫sleep方法)被阻塞了,但是又沒有釋放鎖,其他執行緒便只能乾巴巴地等待。
當有多個執行緒讀寫檔案時,讀操作和寫操作會發生衝突現象,寫操作和寫操作會發生衝突現象,但是讀操作和讀操作不會發生衝突現象。採用synchronized則會導致一個執行緒在進行讀操作,其他執行緒會等待此執行緒讀完。
綜上所述,下synchronized十分的影響效率,上述的這些問題通過使用Lock可以解決。
是JDK5以後才出現的介面。使用Lock是呼叫對應的API。是API層面的鎖
在建立物件時從構造方法傳入true可建立公平鎖,不傳入預設是不公平鎖。
相較synchronized的自動獲得和釋放鎖,Lock需要手動獲得和釋放鎖。
Lock是一個介面,一般使用它的實現類ReentrantLock建立物件來獲取和釋放鎖。