ReentrantLock 1.8解析

ReentrantLock , 可重入鎖, 和 synchronized 有着相同的內存語義

比較下這倆者之間的相同以及區別

相同點:

倆者有着相同的內存語義,

1、當線程獲取鎖時,JMM會把線程對應的本地內存置爲無效,然後臨界區的代碼從主存中讀入共享變量到工作內存。
2、 當線程釋放鎖時,JMM會把該線程對應的本地內存中的共享變量刷新到主內存中。

區別在於,ReentrantLock是應用層面, 是通過volatile來實現同樣的語義

不同點:

synchronized是jvm內置鎖,ReentrantLock是應用層面實現的鎖,所以ReentrantLock在運用方面會更靈活

1、ReentrantLock提供了 鎖查詢(tryLock)、鎖等待 等方法,可以有效的防止死鎖,synchronized只能阻塞了

2、ReentrantLock提供了可以中斷的加鎖機制(lockInterruptibly),synchronized不響應中斷

3、ReentrantLock提供了公平鎖模式,synchronized只有非公平鎖

4、ReentrantLock支持更加靈活的同步代碼塊,但是這也導致了一定的風險,一定要保證在finally中處理鎖釋放,synchronized則沒有這方面擔憂,是由jvm控制

性能方面:

這一塊很多人有誤解,認爲ReentrantLock一定比synchronized要優秀,實際上在Java1.6之前確實是這樣,不過從Java1.6開始,synchronized做了很多性能優化,已經不比ReentrantLock差.
而且隨着jvm的發展和完善,synchronized的性能會越來越好,畢竟它是jvm內置的鎖,在擴展優化方面有着優勢.
java 1.8版的ConcurrentHashMap就是用cas和synchronized完成的

如何選擇

1、大部分情況下,如果ReentrantLock和synchronized都能滿足需求,優先選擇synchronized.
2、如果有鎖嵌套的情況下,可以考慮使用ReentrantLock的鎖查詢、鎖等待等機制,防止死鎖
3、特殊場景需要用到公平鎖,考慮使用ReentrantLock
4、要使用多個條件隊列的情況下,考慮使用ReentrantLock的condition

接下來我們分析下ReentrantLock的源碼,先看下部分類圖結構

摘自java 併發編程的藝術 3.5.3小節

從圖中可以看出ReentrantLock是用內部類(Sync)繼承了AQS, 然後在這個基礎上實現了倆個子類, NonfairSync(非公平鎖),FairSync(公平鎖)

因爲是繼承了AQS,所以ReentrantLock只需要在這個基礎上完善tryAcquire、tryRelease方法即可,接下來以非公平鎖爲例,看下源碼部分

		protected final boolean tryAcquire(int acquires) {
		    return nonfairTryAcquire(acquires);
		}


		//非公平鎖實現具體的tryAcquire邏輯,因爲是獨佔鎖,這裏的acquires傳入1
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //獲取同步狀態state,如果爲0說明此時沒有競爭,可以嘗試獲取同步狀態
            int c = getState();
            if (c == 0) {
                //通過cas 將狀態設置爲tryAcquire
                if (compareAndSetState(0, acquires)) {
                    //因爲是獨佔鎖,所以成功後,記錄下獲取同步狀態的線程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果state不爲0,說明已經有線程獲取了同步狀態,檢查是否是當前線程獲取
            else if (current == getExclusiveOwnerThread()) {
                //如果是的話,則獲取同步狀態,並且state+1,表示重入的次數
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

以上是嘗試獲取同步狀態的方法,成功返回true,失敗返回false, 如果返回false,AQS將會將該線程放入鎖同步隊列中阻塞等待(詳情看這裏AbstractQueuedSynchronizer解析)

		//釋放同步狀態,因爲是獨佔鎖這裏的releases爲1
        //因爲是可重入鎖,這裏state表示重入次數,釋放一次state-1
        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            //如果獲取同步狀態的線程不是當前線程,則拋IllegalMonitorStateException
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果state爲0說明同步狀態已完全釋放
            if (c == 0) {
                free = true;
                //在這裏將之前記錄獲取同步狀態的線程清除
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

上面是嘗試釋放同步狀態的方法,成功返回true,失敗返回false,如果爲true,AQS將會喚醒同步鎖隊列中的線程,繼續競爭同步狀態

ReentrantLock支持公平鎖,看下這部分實現邏輯

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

        //該方法在類FairSync下,邏輯和非公平鎖基本一致
        //唯一的區別是在競爭同步狀態之前,需要判斷隊列中是否有其它正在等待的線程
        //如果有,那麼只能放棄競爭老老實實的進入隊列排在最後面
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //重點在這裏,多了一個hasQueuedPredecessors方法
                //判斷同步鎖隊列中是否還有其他正在等待的節點
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
 }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章