Java ReEntrantLock (Java程式碼實戰-001)

FrankYou發表於2018-05-17

Lock類也可以實現執行緒同步,而Lock獲得鎖需要執行lock方法,釋放鎖需要執行unLock方法

Lock類可以建立Condition物件,Condition物件用來使執行緒等待和喚醒執行緒,需要注意的是Condition物件的喚醒的是用同一個Condition執行await方法的執行緒,所以也就可以實現喚醒指定類的執行緒

Lock類分公平鎖和不公平鎖,公平鎖是按照加鎖順序來的,非公平鎖是不按順序的,也就是說先執行lock方法的鎖不一定先獲得鎖 Lock類有讀鎖和寫鎖,讀讀共享,寫寫互斥,讀寫互斥

 

import java.sql.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReEntrantLockTest
 */
public class ReEntrantLockTest {
    private static final int THREAD_COUNT = 4;
    private static final int WAIT_TIMES = 5;

    public static void main(String[] args) {
        // 預設是非公平鎖
        final ReentrantLock lock = new ReentrantLock();

        final Condition con = lock.newCondition();
        final Runnable add = new Runnable() {
            @Override
            public void run() {
                System.out.println("Time: " + new java.util.Date() + " Pre " + lock);
                // 獲取到鎖的話立即返回,否則一直處於等待狀態
                lock.lock();
                try {
                    // 使當前執行緒處於等待狀態,直到被訊號通知或中斷,此執行緒處於等待狀態,其它執行緒也可以獲取鎖
                    con.await(WAIT_TIMES, TimeUnit.SECONDS);

                    // seleep狀態時則不會釋放鎖
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println("Time:" + new java.util.Date() + " Post " + lock);
                    lock.unlock();
                }
            }
        };

        final ExecutorService exec = Executors.newFixedThreadPool(THREAD_COUNT);
        for (int i = 0; i < THREAD_COUNT; i++) {
            exec.submit(add);
        }
        exec.shutdown();
    }
}

 

ReenTrantLock可重入鎖(和synchronized的區別)總結

可重入性:

從名字上理解,ReenTrantLock的字面意思就是再進入的鎖,其實synchronized關鍵字所使用的鎖也是可重入的,兩者關於這個的區別不大。兩者都是同一個執行緒沒進入一次,鎖的計數器都自增1,所以要等到鎖的計數器下降為0時才能釋放鎖。

鎖的實現:

Synchronized是依賴於JVM實現的,而ReenTrantLock是JDK實現的,有什麼區別,說白了就類似於作業系統來控制實現和使用者自己敲程式碼實現的區別。前者的實現是比較難見到的,後者有直接的原始碼可供閱讀。

效能的區別:

在Synchronized優化以前,synchronized的效能是比ReenTrantLock差很多的,但是自從Synchronized引入了偏向鎖,輕量級鎖(自旋鎖)後,兩者的效能就差不多了,在兩種方法都可用的情況下,官方甚至建議使用synchronized,其實synchronized的優化我感覺就借鑑了ReenTrantLock中的CAS技術。都是試圖在使用者態就把加鎖問題解決,避免進入核心態的執行緒阻塞。

功能區別:

便利性:很明顯Synchronized的使用比較方便簡潔,並且由編譯器去保證鎖的加鎖和釋放,而ReenTrantLock需要手工宣告來加鎖和釋放鎖,為了避免忘記手工釋放鎖造成死鎖,所以最好在finally中宣告釋放鎖。

鎖的細粒度和靈活度:很明顯ReenTrantLock優於Synchronized

ReenTrantLock獨有的能力:

1.      ReenTrantLock可以指定是公平鎖還是非公平鎖而synchronized只能是非公平鎖。所謂的公平鎖就是先等待的執行緒先獲得鎖。

2.      ReenTrantLock提供了一個Condition(條件)類,用來實現分組喚醒需要喚醒的執行緒們,而不是像synchronized要麼隨機喚醒一個執行緒要麼喚醒全部執行緒。

3.      ReenTrantLock提供了一種能夠中斷等待鎖的執行緒的機制,通過lock.lockInterruptibly()來實現這個機制。

ReenTrantLock實現的原理:

在網上看到相關的原始碼分析,本來這塊應該是本文的核心,但是感覺比較複雜就不一一詳解了,簡單來說,ReenTrantLock的實現是一種自旋鎖,通過迴圈呼叫CAS操作來實現加鎖。它的效能比較好也是因為避免了使執行緒進入核心態的阻塞狀態。想盡辦法避免執行緒進入核心的阻塞狀態是我們去分析和理解鎖設計的關鍵鑰匙。

什麼情況下使用ReenTrantLock:

答案是,如果你需要實現ReenTrantLock的三個獨有功能時。

 

相關文章