多執行緒高併發程式設計(2) -- 可重入鎖介紹和自定義

碼猿手發表於2020-04-16

背景:

  什麼是 “可重入”?可重入就是說某個執行緒已經獲得某個鎖,可以再次獲取鎖而不會出現死鎖。即可重入鎖的作用就是為了避免死鎖,java中synchronized和ReentrantLock都是可重入鎖。

  //synchronized 可重入鎖
    private void test() {
        //  第一次獲得鎖
        synchronized (this) {
            while (true) {
                //  第二次獲得同樣的鎖
                synchronized (this) {
                    System.out.println("ReentrantLock!");
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

  //ReentrantLock可重入鎖
    Lock lock = new ReentrantLock();
    private void test1() {
        lock.lock();
        try {
            test2();
        } finally {
            lock.unlock();
        }
    }
    private void test2() {
        lock.lock();
        try {
            System.out.println("ReentrantLock!");
        } finally {
            lock.unlock();
        }
    }

一.自定義不可重入鎖

  所謂不可重入鎖,即若當前執行緒執行某個方法已經獲取了該鎖,那麼在方法中嘗試再次獲取鎖時,就會獲取不到被阻塞。下面的執行緒執行test1()方法首先獲取lock,接下來執行test2()方法就無法執行test2()中的邏輯,必須先釋放鎖。

// 不可重入鎖
class Lock {
    //是否佔用
    private boolean isLocked = false;

    //使用鎖
    public synchronized void lock() {
        while (isLocked) {//已經佔用
            try {
                this.wait();//等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        isLocked = true;//修改標識為已經佔用
    }

    //釋放鎖
    public synchronized void unLock() {
        isLocked = false;//修改標識為未佔用
        notify();//喚醒等待執行緒
    }

}
============使用===========
    Lock lock = new Lock();

    public void test1() {
        lock.lock();
        test2();
        lock.unLock();
    }

    private void test2() {
        lock.lock();
        //...
        lock.unLock();
    }

二.自定義可重入鎖

   流程:

  1. 定義鎖佔用標識、儲存執行緒、執行緒鎖持有數量;
  2. 使用鎖:判斷是否已經佔用和當前執行緒是否不等於儲存執行緒,如果條件符合進入等待,不符合則修改佔用標識、儲存執行緒為當前執行緒、執行緒鎖數量+1;
  3. 釋放鎖:判斷當前執行緒是否等於儲存執行緒,條件符合則執行緒鎖數量-1,當執行緒鎖數量=0時,修改佔用標識,喚醒等待執行緒,將儲存執行緒置為null;
// 可重入鎖
class ReLock{
    //是否佔用
    private boolean isLocked = false;
    private Thread lockedBy = null; //儲存執行緒
    private int holdCount = 0;
    //使用鎖
    public synchronized void lock() throws InterruptedException {
        Thread t = Thread.currentThread();
        while(isLocked && lockedBy != t) {
            wait();
        }
        isLocked = true;
        lockedBy = t;
        holdCount ++;
    }
    //釋放鎖
    public synchronized void unlock() {
        if(Thread.currentThread() == lockedBy) {
            holdCount --;
            if(holdCount ==0) {
                isLocked = false;
                notify();
                lockedBy = null;
            }        
        }        
    }
    public int getHoldCount() {
        return holdCount;
    }
}
==============使用===============
public class LockTest {
    ReLock lock = new ReLock();
    public void test1() throws InterruptedException {
        lock.lock();
        System.out.println(lock.getHoldCount());
        test2();
        lock.unlock();
        System.out.println(lock.getHoldCount());
    }
    //可重入
    public void test2() throws InterruptedException {
        lock.lock();
        System.out.println(lock.getHoldCount());
        //...................
        lock.unlock();
        System.out.println(lock.getHoldCount());
    }
    public static void main(String[] args) throws InterruptedException {
        LockTest lockTest= new LockTest();
        lockTest.test1();            
        Thread.sleep(1000);        
        System.out.println(lockTest.lock.getHoldCount());
    }

}

相關文章