天天看點

java多線程:使用Lock接口實作自定義鎖,互斥鎖與重入鎖

1.Lock接口的三個實作類

java多線程:使用Lock接口實作自定義鎖,互斥鎖與重入鎖
  • ReentrantLock(可重入鎖)
  • ReadLock(讀鎖)
  • WriteLock(寫鎖)

2.自定義互斥鎖實作

  • 通過實作Lock接口的lock()方法和unlock()方法來完成自定義實作
  • ps:主要思想就是設定一個狀态量,根據狀态量來判斷是否鎖被占用,如果被占用就wait(),如果沒被占用就使用。
  • 自定義互斥鎖實作 
public class MyLock implements Lock {

    /**
     * 是否擁有鎖
     */
    private volatile Boolean isHoldLock = false;


    /**
     * 同一時刻,有且僅有一個線程擁有鎖,
     * 其他線程隻有等待擁有鎖的線程釋放鎖才能擷取鎖
     */
    @Override
    public synchronized void lock() {
        // 通過自旋的方式去判斷是否擁有鎖,沒有鎖就進入線程等待
        while (isHoldLock) {
            try {
                wait ();
            } catch (InterruptedException e) {
                e.printStackTrace ();
            }
        }
        // 設定線程擁有鎖
        isHoldLock = true;
    }

    /**
     * 釋放鎖
     */
    @Override
    public synchronized void unlock() {
        // 随機喚醒一個等待的線程
        notify ();
        // 将該持有鎖釋放
        isHoldLock = false;
    }

    // 忽略以下重寫代碼

}
           
  • 測試代碼
public class LockDemo {

    private static Lock lock = new MyLock ();

    private static CountDownLatch countDownLatch = new CountDownLatch (10);

    private static int num = 0;

    public static void inCreate() {
        // 擷取鎖
        lock.lock ();
        num++;
        // 釋放鎖
        lock.unlock ();
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread (() -> {
                for (int j = 0; j < 1000000; j++) {
                    inCreate ();
                }
                // 線程執行結束執行countDown,對計數減一
                countDownLatch.countDown ();
            }).start ();
        }

        while (true) {
            // 所有線程執行完畢,輸出num
            if (countDownLatch.getCount () == 0) {
                System.out.println (num);
                break;
            }
        }

    }

}
           
控制台輸出:
  • 10000000

3.自定義重入鎖實作

  • ps:可重入鎖的實作就是在互斥鎖的基礎上加上線程判斷,并且使用一個reentryCount來記錄鎖的層數,以便于釋放鎖時的判斷。 
  • 自定義重入鎖實作 
public class MyLock implements Lock {

    /**
     * 是否擁有鎖
     */
    private volatile Boolean isHoldLock = false;

    /**
     * 目前擁有鎖的線程
     */
    private Thread holdLockThread = null;

    /**
     * 重入計數
     */
    private int reentryCount = 0;


    /**
     * 同一時刻,有且僅有一個線程擁有鎖,
     * 其他線程隻有等待擁有鎖的線程釋放鎖才能擷取鎖
     */
    @Override
    public synchronized void lock() {
        // 通過自旋的方式去判斷是否擁有鎖,并且是否被目前線程持有,否則就進入線程等待
        while (isHoldLock && Thread.currentThread () != holdLockThread) {
            try {
                wait ();
            } catch (InterruptedException e) {
                e.printStackTrace ();
            }
        }
        // 設定線程擁有鎖
        isHoldLock = true;
        // 給目前線程指派給holdLockThread
        holdLockThread = Thread.currentThread ();
        // 重入次數自增
        reentryCount++;
    }

    /**
     * 釋放鎖
     */
    @Override
    public synchronized void unlock() {
        // 判斷目前線程是否是持有鎖的線程,是,重入次數減去1,不是就不做處理
        if (holdLockThread == Thread.currentThread ()) {
            reentryCount--;
            if (reentryCount == 0) {
                // 随機喚醒一個等待的線程
                notify ();
                // 将該持有鎖釋放
                isHoldLock = false;
            }
        }
    }

    // 忽略其他重寫方法

}
           
  • 測試代碼
public class ReentryDemo {

    private Lock lock = new MyLock ();

    public void methodA(){
        lock.lock ();
        System.out.println (String.format ("線程%s進入方法A",Thread.currentThread ().getName ()));
        methodB ();
        lock.unlock ();
    }

    public void methodB(){
        lock.lock ();
        System.out.println (String.format ("線程%s進入方法B",Thread.currentThread ().getName ()));
        lock.unlock ();
    }

    public static void main(String[] args) {
        new ReentryDemo ().methodA ();
    }

}
           
控制台輸出:
  • 線程main進入方法A
  • 線程main進入方法B
ps:可重入就是說某個線程已經獲得某個鎖,可以再次擷取鎖而不會出現死鎖,叫可重入鎖

4.總結

  • ps:通過實作JDK提供的Lock接口,來自定義鎖的實作,可以根據自己的業務要求編寫,一般情況下使用ReentrantLock就可以了
  • 互斥鎖:主要思想就是設定一個狀态量,根據狀态量來判斷是否鎖被占用,如果被占用就wait(),如果沒被占用就使用
  • 重入鎖:可重入鎖的實作就是在互斥鎖的基礎上加上線程判斷,并且使用一個reentryCount來記錄鎖的層數,以便于釋放鎖時的判斷。 

連結:java多線程:ReentrantLock的使用,實作共享變量線程安全