目錄
- 樂觀鎖
- CAS
- 悲觀鎖
- synchronized
- synchronized的局限性
- Lock
當一個資料被多個執行緒所共同使用,且執行緒并發執行時,我們需要保證保證該資料的準確性,既一個執行緒對資料的操作不會對另一個執行緒產生不合理的影響,
實作的手段基本上是對資料加鎖,當執行緒要對資料進行操作時必須獲得鎖后再進行操作,鎖可分為樂觀鎖和悲觀鎖,
樂觀鎖
樂觀鎖,總是樂觀地假設最好的情況,每次去拿資料的時候都認為別人不會修改這個資料,所以不會上鎖,只會要對資料進行更新時判斷一下在此期間(拿到資料到更新的期間)別人有沒有去更改這個資料,可以使用版本號機制和CAS演算法實作,
CAS
- CAS(Compare And Swap)是一種常見的“樂觀鎖”,大部分的CPU都有對應的匯編指令,它有三個運算元:記憶體地址V,舊值A,新值B,只有當前記憶體地址V上的值是A,B才會被寫到V上,否則操作失敗,
- Java從5.0開始引入了對CAS的支持,與之對應的是 java.util.concurrent.atomic 包下的AtomicInteger、AtomicReference等類,它們提供了基于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等獨占鎖就是悲觀鎖思想的實作,
synchronized
Java中的關鍵字,是由JVM來維護的,是JVM層面的鎖,
是非公平鎖,
- 同步代碼塊
sychronized可用于修飾一個代碼塊,當執行緒想要執行代碼塊中的代碼時必須先取得鎖物件
格式:synchronized(鎖物件){…} - 同步方法
在方法的回傳值前加上synchronized關鍵字,執行緒想要執行改方法必須獲得鎖,
修飾實體方法:獲得的鎖默認是this(當前物件),
修飾靜態方法:獲得的鎖默認是當前類,
synchronized的局限性
如果獲取鎖的執行緒由于要等待一些原因(比如呼叫sleep方法)被阻塞了,但是又沒有釋放鎖,其他執行緒便只能干巴巴地等待,
當有多個執行緒讀寫檔案時,讀操作和寫操作會發生沖突現象,寫操作和寫操作會發生沖突現象,但是讀操作和讀操作不會發生沖突現象,采用synchronized則會導致一個執行緒在進行讀操作,其他執行緒會等待此執行緒讀完,
綜上所述,下synchronized十分的影響效率,上述的這些問題通過使用Lock可以解決,
Lock
是JDK5以后才出現的介面,使用Lock是呼叫對應的API,是API層面的鎖
在創建物件時從構造方法傳入true可創建公平鎖,不傳入默認是不公平鎖,
相較synchronized的自動獲得和釋放鎖,Lock需要手動獲得和釋放鎖,
- 獲取鎖
- Lock()方法
就是用來獲取鎖,如果鎖已被其他執行緒獲取,則進行等待, - tryLock()方法
此表示用來嘗試獲取鎖,如果獲取成功,則回傳true,如果獲取失敗(即鎖已被其他執行緒獲取),則回傳false,這個方法會立即回傳true或false,在拿不到鎖時不會一直在那等待, - tryLock(long time, TimeUnit unit)方法
與tryLock()方法類似,區別在于這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就回傳false,如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則回傳true, - lockInterruptibly()方法
此方法優先考慮回應中斷,而不是回應鎖的獲取,也就是說如果執行緒獲取不到鎖則可以通過呼叫interrupt()方法中斷執行緒,
- Lock()方法
- 釋放鎖
- unLock()方法
Lock是一個介面,一般使用它的實作類ReentrantLock創建物件來獲取和釋放鎖,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/173242.html
標籤:其他
