上一節講了sychronized用法及實現原理,詳細請回顧《 》,今天我們繼續講解ReentrantLock。
ReentrantLock的介紹
ReentrantLock重入鎖,是實現Lock接口的一個類,也是在實際編程中使用頻率很高的一個鎖,支持重入性,表示能夠對共享資源能夠重複加鎖,即當前線程獲取該鎖再次獲取不會被阻塞。在java關鍵字synchronized隱式支持重入性(關於synchronized上節已經講過),synchronized通過獲取自增,釋放自減的方式實現重入。與此同時,ReentrantLock還支持公平鎖和非公平鎖兩種方式。
lock 與sychronized的區別
- sychronized是一個關鍵字,Lock是一個對象
- sychronized的鎖釋放是被動的,只有執行完或出現異常時才會釋放。lock可以判斷鎖的判斷
- lock可以指定是公平鎖或非公平鎖,sychronized是非公平鎖,不能指定
重入讀寫鎖 ReentrantReadWriteLock的作用是什麼
當我們在執行讀操作的時候,它首先獲取一個讀鎖。在併發訪問時,讀它不會被阻塞,因為讀不會改變數據本身的狀態。寫操作時線程必須要獲取一個寫鎖,當其它線程持有寫鎖的情況下,當前線程在寫操作時會獲取不到這個鎖,那麼這個鎖會被阻塞,只有這個鎖被釋放以後其它的寫才可以繼續。同樣,當前線程獲得到寫鎖後,其它線程在讀操作時會被阻塞,因為在寫鎖時有可能數據會發生變化。在寫鎖釋放後讀操作才可以進入讀取數據。
其實就是通過讀寫鎖來提升併發操作的性能,使用讀寫鎖能夠在讀多寫少的場景下,大大提升我們操作性能。
讀-讀是共享
讀-寫是不共享
寫-讀是不共享
寫-寫是不共享
談到ReentrantLock,不得不談的AbstractQueuedSynchronizer。
ReentrantLock實現圖
ReentrantLock非公平鎖和公平鎖的源碼
//非公平鎖
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//公平鎖
final void lock() {
acquire(1);
}
ReentrantLock提供兩個構造方法
//默認是非公平鎖
public ReentrantLock() {
sync = new NonfairSync();
}
//
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
什麼是公平和非公平鎖
公平鎖每次獲取到鎖為同步隊列中的第一個節點,保證請求資源時間上的絕對順序,而非公平鎖有可能剛釋放鎖的線程下次繼續獲取該鎖,則有可能導致其他線程永遠無法獲取到鎖,造成“飢餓”現象。
公平鎖為了保證時間上的絕對順序,需要頻繁的上下文切換,而非公平鎖會降低一定的上下文切換,降低性能開銷。因此,ReentrantLock默認選擇的是非公平鎖,則是為了減少一部分上下文切換,保證了系統更大的吞吐量。
Condition線程間通信
他提供的是在JDK層面上實現對鎖的控制。在多線程中用來協調通信的工具,跟wait/notify是完全一樣的。也就是可以讓某些線程在某個條件的情況下,只有滿足某個條件的時候線程才會被喚醒,若不滿足,則不會喚醒。
Condition的實現原理和基本使用方法
1、Condition提供了await()方法將當前線程阻塞,並提供signal()方法支持另外一個線程將已經阻塞的線程喚醒。
2、Condition需要結合Lock使用
3、線程調用await()方法前必須獲取鎖,調用await()方法時,將線程構造成節點加入等待隊列,同時釋放鎖,並掛起當前線程
4、其他線程調用signal()方法前也必須獲取鎖,當執行signal()方法時將等待隊列的節點移入到同步隊列,當線程退出臨界區釋放鎖的時候,喚醒同步隊列的首個節點
為什麼在調用condition.await()和condition.signal()時要獲取鎖?
是因為需要通過這個線程等待釋放後仍然能夠競爭鎖,Condition隊列喚醒後重新加入AQS
AQS的介紹
提供了一個基於FIFO隊列,可以用於構建鎖或者其他相關同步裝置的基礎框架。該同步器(以下簡稱同步器)利用了一個int來表示狀態,期望它能夠成為實現大部分同步需求的基礎。使用的方法是繼承,子類通過繼承同步器並需要實現它的方法來管理其狀態,管理的方式就是通過類似acquire和release的方式來操縱狀態。然而多線程環境中對狀態的操縱必須確保原子性,因此子類對於狀態的把握,需要使用這個同步器提供的以下三個方法對狀態進行操作:
- getState()
- setState(int)
- compareAndSetState(int, int)
AQS定義兩種資源共享方式:Exclusive(獨佔,只有一個線程能執行,如ReentrantLock)和Share(共享,多個線程可同時執行,如Semaphore/CountDownLatch)
AQS中的Node是核心,它是一個鏈表,AQS 有兩個鎖 共享鎖和獨佔鎖
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
鏈表的頭節點和尾節點,源碼
private transient volatile Node head;
private transient volatile Node tail;
對應CAS的方法
compareAndSetHead()
compareAndSetTail()
CompareAndSet 的實現是都是由 Unsafe.compareAndSwapObject 實現,其中Unsafe是jvm中的後門。
compareAndSetState方法中
- state=0 表示無鎖
- state>0表示有鎖
private volatile long state;
喚醒線程的方式
- Thread.currentThread().interrupted()
- LockSupport.unpark()
------------------
閱讀更多 零售雲技術內參 的文章