總結先寫前面: 後面留給你去驗證
在AQS當中:
主要有五個值得關注的點:
- private volatile int state; 鎖狀態的標誌位0 爲沒有上鎖。大於0 表示 加鎖 值標識鎖重入的次數。
- cas原子操作 保證了交換鎖標誌位的值,以及入隊列等操作在高併發條件下不會出錯。
- LockSupport 提供了將線程阻塞跟喚醒的操作,避免了無謂的自旋操作。
- 隊列 存儲所有阻塞的等待喚醒的線程。以便在釋放鎖的時候使用LockSuport來喚醒它們。
- 自旋
1. NonfairSync#lock()
分析階段
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
這裏面調用了兩個方法:
1> 一個cas操作 如果設置成功 那就獲得鎖 並設置當前持有的線程爲當前線程。可以直接獲得執行的權利,不用去與其他線程來競爭執行的權利
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
2> 設置 線程爲持有線程
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
3> 獲取鎖失敗參與鎖競爭
public final void acquire(int arg) {
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
這裏先引入公平鎖 非公平鎖的概念:
公平鎖:所有的鎖都通過競爭來獲取鎖
非公平鎖: 鎖可以先嚐試獲取鎖 ,如果獲取成功就不用參與競爭,第一次獲取鎖失敗就參與競爭。
acquire(1);acquire方法做了兩件事:嘗試獲取鎖,嘗試失敗就放到等待隊列裏面。
我們先討論非公平鎖,它的tryAcquire:
final boolean nonfairTryAcquire(int acquires) {
//拿到當前線程
final Thread current = Thread.currentThread();
int c = getState();
//當前狀態是未加鎖
if (c == 0) {
//acquires = 1 設置成1,嘗試獲取 獲取不到也沒事。爲true 就是獲取鎖成功
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}//如果當前線程是執行線程。
else if (current == getExclusiveOwnerThread()) {
//每次都 + 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//賦值
setState(nextc);
return true;
}
//獲取鎖失敗
return false;
}
nonfairTryAcquire總結:非公平鎖的如果當前鎖未被線程持有那就先持有,如果是第二次獲取當前鎖state++ ,可重入的鎖。
我們先分析完添加到隊列的方法然後再看如何解鎖將state 變爲 0 的.
將當前線程創建成一個Node 並且入隊列。
private Node addWaiter(Node mode) {
//排他鎖Node.EXCLUSIVE,創建節點
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
// 比較快的方式入隊列,放到尾部
Node pred = tail;
if (pred != null) {
node.prev = pred;
//可以設置
if (compareAndSetTail(pred, node)) {
//改變指針
pred.next = node;
return node;
}
}
// 如果隊列爲空,入隊列
enq(node);
return node;
}
自旋入隊列
private Node enq(final Node node) {
for (;;) {
//尾部大概率是空的
Node t = tail;
if (t == null) { // Must initialize
//頭設置一個空的節點
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
添加到隊列不是很複雜,但是下面的纔是複雜的。
//以排他的不間斷模式獲取已排隊的線程。>>>>>>>>>>>>>>>>
final boolean acquireQueued(final Node node, int arg) {
//獲取鎖標誌
boolean failed = true;
try {
//中斷標誌位
boolean interrupted = false;
//自旋
for (;;) {
final Node p = node.predecessor();
//從頭開始獲取
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//獲取失敗 然後使用LockSupport#park方法阻塞當前線程
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
//可以中斷的自旋
interrupted = true;
}
} finally {
//如果獲取不到 放棄獲取
if (failed)
cancelAcquire(node);
}
}
2.unlock
public final boolean release(int arg) {
//嘗試釋放鎖
if (tryRelease(arg)) {
//頭結點
Node h = head;
//頭節點不爲空
if (h != null && h.waitStatus != 0)
//解除阻塞
unparkSuccessor(h);
return true;
}
return false;
}
嘗試釋放鎖
protected final boolean tryRelease(int releases) {
//state --
int c = getState() - releases;
//
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//鎖釋放了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//設置state狀態
setState(c);
return free;
}
從線程隊列中取出線程然後LockSupport.unpark()解除被阻塞的線程。
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
//頭節點是null的
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
//下一個被喚醒的節點不爲空就喚醒它
if (s != null)
LockSupport.unpark(s.thread);
}