前面已經講解了關於AQS的非公平鎖模式,關於NonfairSync
非公平鎖,內部其實告訴咱們誰先爭搶到鎖誰就先得到資源,下面就來分析一下公平鎖FairSync
內部是如何實現公平的?若是沒有看過非公平鎖的先去了解下非公平鎖,由於這篇文章前面不會講太多內部結構,直接會對源碼進行分析java
前文鏈接地址:圖解AQS原理之ReentrantLock詳解-非公平鎖segmentfault
本文分析的JDK版本是1.8舒適提示:讀本文內容建議結合以前寫的非公平,前篇設計了不少基礎性內容less
在源碼分析以前,咱們先來看一下ReentrantLock
如何切換獲取鎖的模式呢?實際上是在構造器中傳遞指定的類型變量來控制使用鎖的方式,以下所示:函數
public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
當fair
參數指定爲true時,表明的是公平鎖,若是指定爲false則使用的非公平,無參的構造函數默認使用的是非公平模式,以下所示:源碼分析
public ReentrantLock() { sync = new NonfairSync(); }
接下來咱們以一個例子來進行後面的說明:ui
public class ReentrantLockDemo { public static void main(String[] args) throws Exception { AddDemo runnalbeDemo = new AddDemo(); Thread thread = new Thread(runnalbeDemo::add); thread.start(); Thread.sleep(500); Thread thread1 = new Thread(runnalbeDemo::add); thread1.start(); System.out.println(runnalbeDemo.getCount()); } private static class AddDemo { private final AtomicInteger count = new AtomicInteger(); private final ReentrantLock reentrantLock = new ReentrantLock(true); private final Condition condition = reentrantLock.newCondition(); private void add() { try { reentrantLock.lockInterruptibly(); count.getAndIncrement(); } catch (Exception ex) { System.out.println("線程被中斷了"); } finally { // reentrantLock.unlock(); } } int getCount() { return count.get(); } } }
咱們經過源碼能夠看到這裏咱們啓動了兩個線程,兩個線程分別進行同步鎖操做,這裏我並無釋放掉鎖,由於方便分析隊列的狀況,固然你也能夠在內部寫一個死循環,不釋放鎖就能夠了,我這裏簡單的不釋放鎖,使用的是可中斷的獲取鎖操做方法lockInterruptibly
,這裏內部的原理咱們上一篇文章中已經講解過了,這裏並不過多的去分析內部原理,這個ReentrantLock
的lockInterruptibly
調用內部類AQS
的acquireInterruptibly
,可是實際上是FairSync
內部類繼承了內部類Sync
,而內部類Sync
有繼承了AbstractQueuedSynchronizer
簡稱AQS,acquireInterruptibly
源碼信息以下所示:this
public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
這裏咱們經過上一篇文章得知tryAcquire
是須要子類去實現的方法,咱們在例子中指定了使用的是公平鎖,因此tryAcquire
方法的實現是在ReentrentLock
的FairSync
類中,咱們來具體看一下這個方法,重點也在這個方法中其餘的其實都是同樣的,由於用的方法都會同樣的非公平和公平鎖的調用,惟獨不同的就是子類實現的方法是不相同的,接下來咱們就來看一下公平鎖的tryAcquire
是如何實現的?線程
protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && //判斷是否有等待的線程在隊列中 compareAndSetState(0, acquires)) { //嘗試爭搶鎖操做 setExclusiveOwnerThread(current); //設置當前線程獨佔鎖資源 return true; //得到鎖成功 } } else if (current == getExclusiveOwnerThread()) { //當前線程和獨佔鎖資源的線程一致,則能夠重入 int nextc = c + acquires; //state遞增 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); //設置state狀態 return true; //得到鎖成功 } return false; //得到鎖失敗 }
對比非公平鎖的NonfairSync
類的tryAcquire
方法,其實就是在鎖可用的狀況下增長了一個判斷條件,這個判斷方法就是hasQueuedPredecessors
,從方法的名稱來看說的是有等待的線程隊列,換句話說已經有人在排隊了,新來的線程你就不能加塞,而非公平模式的誰先爭搶到鎖就是誰的,管你先來不先來,接下來咱們具體看一下這個設計
hasQueuedPredecessors
方法源碼:code
public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // 得到尾節點 Node h = head; // 得到頭節點 Node s; return h != t && //頭節點和尾節點相同表明隊列爲空 ((s = h.next) == null || s.thread != Thread.currentThread()); //頭節點的next節點爲空表明頭節點,以及s.thread不是當前線程不是本身的話表明隊列中存在元素 }
經過上面的源碼信息,能夠得出其實內部主要就是判斷有沒有排隊等待的節點,隊列是否爲空,若是爲空的話則能夠爭搶鎖,若是隊列不爲空,夥計你必須老老實實給我排隊去,除非佔有鎖的線程和請求鎖的線程是同樣的,不然仍是老老實實排隊去,這就是公平模式的鎖操做,還有一個lock
方法,公平模式的lock
方法,沒有直接上來先獲取鎖,而是先嚐試得到鎖直接調用AQS
的aquire
方法進行嘗試獲取鎖,下面是FairSync
源碼:
static final class FairSync extends Sync { private static final long serialVersionUID = -3000897897090466540L; final void lock() { acquire(1); //這裏直接調用了aquire並無嘗試修改state狀態 } /** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { 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; } }
本內容主要是結合上篇內容的一個續篇,能夠結合上篇而後再看下篇會比較清晰些。