ReentrantLock(重入鎖)

ReentrantLock

先看一段程序:

public class DemoT {
    //    定義一個共享變量
    private static int count = 0;

    //  用來操作共享變量的方法
    public static void inc() {
        try {
//           爲了展示出多線程同時操作同一數據會出現問題。如果去掉之後可能會正常計算出數據
            Thread.sleep(1);
            count++;
//            decr();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
    }


    public static void main(String[] args) throws InterruptedException {
//        新建三個線程 每個循環100遍,去調用inc方法操作共享變量
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread3.start();
//        保證三個子線程已經運行結束
        Thread.sleep(2000);
//        打印共享變量
        System.out.println("count:" + count);
    }
}

想要的結果:(打印:count:300)

實際打印結果:count:285(可能不是285,但是基本上都是小於300)

添加重入鎖(ReentrantLock)(還是之前的代碼只不過加了一把鎖)

public class DemoT {
    //    定義一個共享變量
    private static int count = 0;

    //添加重入鎖   @param fair {@code true} if this lock should use a fair ordering policy(譯:@param fair{@code true}如果此鎖應使用公平排序策略)
//    如果添加true 則是使用公平鎖,否則爲非公平鎖
    static Lock lock = new ReentrantLock(true);


    //  用來操作共享變量的方法
    public static void inc() {
        lock.lock(); //獲得鎖(互斥鎖)  
        try {
//           爲了展示出多線程同時操作同一數據會出現問題。如果去掉之後可能會正常計算出數據
            Thread.sleep(1);
            count++;
//            decr();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//釋放鎖
        }
    }


    public static void main(String[] args) throws InterruptedException {
//        新建三個線程 每個循環100遍,去調用inc方法操作共享變量
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread3.start();
//        保證三個子線程已經運行結束
        Thread.sleep(2000);
//        打印共享變量
        System.out.println("count:" + count);
    }
}

運行得到:count:300

實現想要的結果,所以這是一個互斥鎖,只允許一個線程進入當前範圍

那爲什麼他叫“重入”呢?是因爲他可以獲得當前鎖的情況下,還可以進入另外一個以當前鎖爲鎖頭的代碼塊。例如下面代碼:(還是之前代碼,只不過添加了一個方法體用來對共享變量遞減)


public class DemoT {
    //    定義一個共享變量
    private static int count = 0;

    //添加重入鎖   @param fair {@code true} if this lock should use a fair ordering policy(譯:@param fair{@code true}如果此鎖應使用公平排序策略)
//    如果添加true 則是使用公平鎖,否則爲非公平鎖
    static Lock lock = new ReentrantLock(true);


    //  用來操作共享變量的方法
    public static void inc() {
        lock.lock(); //獲得鎖(互斥鎖)  
        try {
//           爲了展示出多線程同時操作同一數據會出現問題。如果去掉之後可能會正常計算出數據
            Thread.sleep(1);
            count++;
            decr();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();//釋放鎖
        }
    }

    public static void decr() {
        lock.lock(); //state=2   //ThreadA再次來搶佔鎖 : 不需要再次搶佔鎖,而是隻增加重入的次數
        try {
            count--;
        } finally {
            lock.unlock(); //state=1
        }
    }

    public static void main(String[] args) throws InterruptedException {
//        新建三個線程 每個循環100遍,去調用inc方法操作共享變量
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread.start();
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread2.start();
        Thread thread3 = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                DemoT.inc();
            }
        });
        thread3.start();
//        保證三個子線程已經運行結束
        Thread.sleep(2000);
//        打印共享變量
        System.out.println("count:" + count);
    }
}

預計結果:count:0

實際運行結果:count:0

概念

重入是如何實現的?

內部有一個 volatile 修飾的變量,噹噹前線程添加一把鎖變量+1 ,當變量結束一個鎖,則-1;當爲0 說明當前線程無當前鎖
 volatile int waitStatus;

沒有獲取到當前鎖的線程怎麼操作?以及是怎麼保存的?

會放在一個雙向鏈表 使用 addWaiter 將未獲得鎖的線程加入到隊列 

並且區分公平鎖和非公平鎖

非公平鎖:如果當前線程執行到鎖的位置,直接去搶佔鎖,如果搶到,直接拿到鎖,運行。鏈表隊列中繼續等待。

公平鎖:如果當前線程執行到鎖的位置,先去鏈表中判斷是否存在其他線程,如果存在,加入隊列末尾。如果不存在,去判斷鎖是否被其他線程拿到,如果有,添加到鏈表隊列,沒有執行代碼塊。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章