簡單聊聊Java中的ReentrantLock

深夜程猿發表於2018-03-07

【福利】關注微信公眾號:深夜程猿,回覆關鍵字即可獲取學習視訊、簡歷模版

簡單聊聊Java中的ReentrantLock

歡迎讀者批評指正~~

背景知識

在Java中,要想做到執行緒同步的傳統方式是使用關鍵字synchronized。synchronized關鍵字提供了一個基本的同步機制,但是它的使用是十分嚴格的。例如,一個執行緒只能獲取一次鎖。同步的阻塞沒有提供任何阻塞佇列機制,當一個執行緒退出的時候,任何的執行緒都可以獲取鎖。這樣會導致某些執行緒在一段長時間內處於飢餓狀態(獲取不到鎖,無法訪問資源)。可重入鎖(Reentrant Lock)的引入使得解決這方面的問題變得簡便,處理Java的執行緒同步問題也會加靈活。

什麼是可重入鎖

可重入鎖實現了Lock介面,提供了一些共享資源的方法。操作共享資源的程式碼處於lock方法和unlock方法的呼叫之間。這使得當前工作執行緒獲取鎖,阻塞其他想要獲取共享資源鎖的執行緒。 正如名字那樣,可重入鎖使得執行緒可以多次進入資源鎖(就是佔有該資源的執行緒在為完全釋放資源鎖的時候,還可以再一次進入該資源鎖)。當執行緒第一次進入鎖的時候,會設定一個鎖計數值(hold count)為1,線上程未釋放鎖的時候可以再一次進入該鎖,鎖計數值遞增.對於每一次釋放鎖的請求,鎖計數值會遞減,直到鎖計數值為0的時候,資源才會真正被釋放。 可重入鎖也會提供一個公平的引數,使得鎖遵守鎖的請求順序。比如,在一個執行緒釋放資源的時候,將鎖分配給等待最長時間的執行緒。在構造可重入鎖的時候,傳入true就可以設定公平模式。 unlock方法要放在finally語句中呼叫,保證任何情況都會釋放鎖。

ReentrantLock的常用方法

lock()

呼叫lock方法,如果資源鎖未被其它執行緒佔用(當前執行緒佔用還可以再一次獲取鎖)就獲取資源鎖,鎖計數值遞增,執行緒獲得資源鎖並立即;如果資源鎖被其它執行緒佔用,那麼就進入休眠狀態,直到獲取到資源鎖,同時鎖計數值設為1;

tryLock()

呼叫tryLock方法,如果資源沒有被其它任何執行緒佔有(當前執行緒佔用還可以再一次獲取鎖)就獲取資源鎖,返回true並遞增鎖計數值;如果資源鎖被其它執行緒佔用佔用,返回false,執行緒不會退出,而是執行完run方法邏輯再嘗試獲取資源鎖,依次下去,直到獲取到資源鎖;

tryLock(long timeout, TimeUnit unit)

作用和tryLock一樣,只是為獲取到資源鎖的時候,執行緒會等待(不斷嘗試獲取資源鎖),超過設定的超時時間才會返回false;

lockInterruptibly()

呼叫這個方法,如果資源沒有被其它任何執行緒佔有(當前執行緒佔用還可以再一次獲取鎖)就獲取資源鎖,遞增鎖計數值;如果再獲取鎖的過程,當前執行緒被其它執行緒interrupt(其它執行緒呼叫當前執行緒的interrupt()方法),那麼就會要求處理InterruptedException;如果當前執行緒沒有獲取到鎖,就會休眠。休眠過程,如果其它執行緒interupt當前執行緒,也會要求處理InterruptedException

unlock()

呼叫unlock方法會對鎖計數值遞減,只有鎖計數值的值是0的時候才會釋放資源

演示demo

public class Test {

    static class Worker implements Runnable {
        String name;
        ReentrantLock re;

        public Worker(ReentrantLock rl, String n) {
            re = rl;
            name = n;
        }

        public void run() {
            boolean done = false;
            while (!done) {
                //獲取外部鎖
                System.out.println("task name - " + name + " try to acquired lock by tryLock()");
                boolean ans = re.tryLock();
                if (ans) {
                    // 獲取到鎖
                    try {
                        Date d = new Date();
                        SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                        System.out.println("task name - " + name
                                + " outer lock acquired at "
                                + ft.format(d)
                                + " Doing outer work");
                        Thread.sleep(1500);

                        // 獲取內部鎖
                        System.out.println("task name - " + name + " try to acquired lock by lock()");
                        re.lock();
                        try {
                            d = new Date();
                            ft = new SimpleDateFormat("hh:mm:ss");
                            System.out.println("task name - " + name
                                    + " inner lock acquired at "
                                    + ft.format(d)
                                    + " Doing inner work");
                            System.out.println("Lock Hold Count - " + re.getHoldCount());
                            Thread.sleep(1500);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            //釋放內部鎖
                            System.out.println("task name - " + name +
                                    " releasing inner lock");

                            re.unlock();
                        }
                        System.out.println("Lock Hold Count - " + re.getHoldCount());
                        System.out.println("task name - " + name + " work done");

                        done = true;
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        //釋放外部鎖
                        System.out.println("task name - " + name +
                                " releasing outer lock");

                        re.unlock();
                        System.out.println("Lock Hold Count - " +
                                re.getHoldCount());
                    }
                } else {
                    // 沒有獲取到鎖
                    System.out.println("task name - " + name +
                            " waiting for lock");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    static final int MAX_T = 2;
    public static void main(String[] args) {
        ReentrantLock rel = new ReentrantLock();
        ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
        Runnable w1 = new Worker(rel, "Job1");
        Runnable w2 = new Worker(rel, "Job2");
        Runnable w3 = new Worker(rel, "Job3");
        Runnable w4 = new Worker(rel, "Job4");
        pool.execute(w1);
        pool.execute(w2);
        pool.execute(w3);
        pool.execute(w4);
        pool.shutdown();
    }
}
複製程式碼
輸出
task name - Job1 try to acquired lock by tryLock()
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 outer lock acquired at 05:29:50 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 try to acquired lock by lock()
task name - Job1 inner lock acquired at 05:29:51 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job1 releasing inner lock
Lock Hold Count - 1
task name - Job1 work done
task name - Job1 releasing outer lock
Lock Hold Count - 0
task name - Job3 try to acquired lock by tryLock()
task name - Job3 outer lock acquired at 05:29:53 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 try to acquired lock by lock()
task name - Job3 inner lock acquired at 05:29:54 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job3 releasing inner lock
Lock Hold Count - 1
task name - Job3 work done
task name - Job3 releasing outer lock
Lock Hold Count - 0
task name - Job4 try to acquired lock by tryLock()
task name - Job4 outer lock acquired at 05:29:56 Doing outer work
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 try to acquired lock by lock()
task name - Job4 inner lock acquired at 05:29:57 Doing inner work
Lock Hold Count - 2
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job2 try to acquired lock by tryLock()
task name - Job2 waiting for lock
task name - Job4 releasing inner lock
Lock Hold Count - 1
task name - Job4 work done
task name - Job4 releasing outer lock
Lock Hold Count - 0
task name - Job2 try to acquired lock by tryLock()
task name - Job2 outer lock acquired at 05:30:00 Doing outer work
task name - Job2 try to acquired lock by lock()
task name - Job2 inner lock acquired at 05:30:01 Doing inner work
Lock Hold Count - 2
task name - Job2 releasing inner lock
Lock Hold Count - 1
task name - Job2 work done
task name - Job2 releasing outer lock
Lock Hold Count - 0
複製程式碼

相關文章