在多執行緒編程中,鎖是常用地控制并發的機制,對于臨界區的資源,需要保證執行緒之間互斥地訪問,
1. 可重入鎖
可重入鎖,也叫做遞回鎖,指的是多次對同一個鎖進行加鎖操作,都不會阻塞執行緒,實作思路:記錄當前鎖正在被哪個執行緒使用,采用計數來統計lock和unlock的呼叫次數,正常情況下,lock和unlock的呼叫次數應該相等,如果不相等就會死鎖,
public class Test implements Runnable {
ReentrantLock lock = new ReentrantLock(); //定義一個可重入鎖
public void get() {
lock.lock(); //第一次呼叫lock()
System.out.println(Thread.currentThread().getId());
set();
lock.unlock();
}
public void set() {
lock.lock(); //第二次呼叫lock(),而且會成功,說明lock是可重入鎖
System.out.println(Thread.currentThread().getId());
lock.unlock();
}
@Override
public void run() {
get();
}
public static void main(String[] args) {
Test ss = new Test();
new Thread(ss).start();
new Thread(ss).start();
new Thread(ss).start();
}
}
2. 自旋鎖
首先,看看初級的自旋鎖實作方式:
public class SpinLock {
private AtomicReference<Thread> owner =new AtomicReference<>();
public void lock(){
Thread current = Thread.currentThread();
while(!owner.compareAndSet(null, current)){
}
}
public void unlock (){
Thread current = Thread.currentThread();
owner.compareAndSet(current, null);
}
}
實作思路:通過CAS(CompareAndSet)原子操作來更新變數,如果CAS回傳true,表示獲得了鎖;否則,需要通過while回圈檢查,直到獲得鎖為止,這也是為什么叫做自旋鎖的原因,需要不停的嘗試獲取鎖,
2.1 初級版本的問題
- 同一執行緒前后兩次呼叫lock(),會導致第二次呼叫lock時進行自旋,產生了死鎖(因為第一次呼叫lock()之后,還沒有unlock),說明這個鎖不是可重入的,
- 如果問題一已經解決,當第一次呼叫unlock()時,就已經將鎖釋放了,實際上不應釋放鎖,
2.2 解決方案
- 針對問題一:在lock函式內,應驗證執行緒是否為已經獲得鎖的執行緒
- 針對問題二:采用計數進行統計
public class SpinLock {
private AtomicReference<Thread> owner =new AtomicReference<>();
private int count =0;
public void lock(){
Thread current = Thread.currentThread();
if(current==owner.get()) {
count++;
return ;
}
while(!owner.compareAndSet(null, current)){
}
}
public void unlock (){
Thread current = Thread.currentThread();
if(current==owner.get()){
if(count!=0){
count--;
}else{
owner.compareAndSet(current, null);
}
}
}
}
3. 參考資料
- Java鎖的種類以及辨析(四):可重入鎖
- 面試必問的CAS,你懂了嗎?
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/21112.html
標籤:其他
上一篇:K8S ? K3S !
