結構
首先看看ReentranLock結構,它實現Lock接口,也擁有Sync ,NonfairSync, FairSync三個內部類,
( NonfairSync, FairSync )-----繼承-------> Sync ------繼承-------> AbstractQueuedSynchronizer
NonfairSync ,FairSync 重寫了AQS 的 tryAcquire
構造方式:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock 默認實現非公平鎖,構造爲true時才構造公平鎖
Nonfair.lock
ReentrantLock.class
public void lock() {
sync.acquire(1);
}
AQS.class
public final void acquire(int arg) {
// 嘗試獲取不成功 並且加入隊發現線程被打斷了,
// 設置當前線程打斷了
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
非公平鎖上鎖時,調用sync的acquire,sync的acquire實則繼承AQS的acquire方法,
獲取:先嚐試獲取,不成功再加入等待隊
最終的實現tryAcquire 就是ReetranLock.Sync.nonfairTryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
// 當前線程
final Thread current = Thread.currentThread();
// 獲取AQS狀態值
int c = getState();
if (c == 0) {
// 沒鎖,cas ,直接獲取鎖
if (compareAndSetState(0, acquires)) {
// 將當前線程設置爲鎖的擁有線程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 當前線程就是擁有鎖的線程 (重入) 記錄重入次數 ++
int nextc = c + acquires;
if (nextc < 0)
// overflow
throw new Error("Maximum lock count exceeded");
// 更新狀態值
setState(nextc);
return true;
}
return false;
}
加入等待隊列
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
Node.EXCLUSIVE = null 標記 獨佔模式節點
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
//獲取尾節點
Node oldTail = tail;
if (oldTail != null) {
// 尾節點不爲空 證明當前隊列有值
// 將尾節點(tail)設置爲node的前節點
node.setPrevRelaxed(oldTail);
// cas 將尾節點設置爲node
if (compareAndSetTail(oldTail, node)) {
// 之前尾節點的下一個節點指向 node
oldTail.next = node;
// 處理成功 返回node
return node;
}
} else {
// 隊列爲空 初始化隊
initializeSyncQueue();
}
}
}
// 初始化同步隊列
private final void initializeSyncQueue() {
Node h;
if (HEAD.compareAndSet(this, null, (h = new Node())))
tail = h;
}
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
for (;;) {
// 節點的上一個節點
final Node p = node.predecessor();
// 上一節點指向頭節點 並且 再一次嘗試獲取
if (p == head && tryAcquire(arg)) {
// 此時獲取成功
setHead(node);
p.next = null; // help GC
// 沒有被打斷
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
// 嘗試失敗--->阻塞 ---->繼續驗證是否被打斷
interrupted = true;
}
} catch (Throwable t) {
// 發生了異常 取消嘗試
cancelAcquire(node);
throw t;
}
}
// 節點的上一個節點
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 嘗試失敗後是否阻塞
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* 前節點已經是等待觸發狀態
*/
return true;
if (ws > 0) {
/*
* 前節點取消了排隊
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
// 尋找沒取消的前。。。前節點
pred.next = node;
} else {
// 將上一節點設置爲等待觸發
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
從源碼中看出 作者編程一般使用 && 來判斷是否進行下一步
用死循環遞歸
1.嘗試獲取 成功就結束了,不成功:加入隊列;
2.先加入等待隊列,
1)隊列操作:
隊列有節點,直接入隊,將當天節點設置爲尾節點,
隊列沒節點,初始化一個空隊列,首尾相同 head = tail ,進行上面操作。
2)再次嘗試,設置等待狀態 ,去除取消的節點,判斷當前被打斷
再次嘗試:如果這是前面沒有節點了,也就是頭節點是當前節點的前置節點,再次嘗試獲取
設置等待狀態(判斷前節點狀態):等待觸發-->判斷被打斷;取消--->設置節點的前節點是未取消的前面節點;初始或其他狀態-->將前節點設置爲等待觸發 ,然後在進行上面操作循環。
判斷被打斷: