可重入鎖 自旋鎖,看這一篇就夠了!

在多線程編程中,鎖是經常使用地控制併發的機制,對於臨界區的資源,須要保證線程之間互斥地訪問。java

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 初級版本的問題
  1. 同一線程先後兩次調用lock(),會致使第二次調用lock時進行自旋,產生了死鎖(由於第一次調用lock()以後,尚未unlock),說明這個鎖不是可重入的。
  2. 若是問題一已經解決,當第一次調用unlock()時,就已經將鎖釋放了。實際上不該釋放鎖。
2.2 解決方案
  1. 針對問題一:在lock函數內,應驗證線程是否爲已經得到鎖的線程
  2. 針對問題二:採用計數進行統計
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. 參考資料

  1. Java鎖的種類以及辨析(四):可重入鎖
  2. 面試必問的CAS,你懂了嗎?
相關文章
相關標籤/搜索