斜體為抽象類,下橫線為介面
聚合關係總結:
- ReentrantLock實現了Lock,Serializable介面
- ReentrantLock.Sync(內部類)繼承了AQS
- ReentrantLock.NonfairSync和ReentrantLock.FairSync繼承了ReentrantLock.Sync
- ReentrantLock持有ReentrantLock.Sync物件(實現鎖功能)
鎖實現總結:
- 由Node節點組成一條同步佇列(有head,tail兩個指標,並且head初始化時指向空節點)
- int state標記鎖使用數量(獨佔鎖時,通常為1,發生重入時>1)
- lock()時加到佇列尾部
- unlock()時,釋放head節點,並指向下一個節點head=head.next,然後喚醒當前head節點
性質:
- 獨佔鎖(排它鎖):只能有一個執行緒獲取鎖
- 重入鎖:一個執行緒可以多次lock()
- 公平/非公平鎖:只針對上鎖過程
- 非公平鎖:嘗試獲取鎖,若成功立刻返回,失敗則加入同步佇列
- 公平鎖:直接加入同步佇列
Lock
Lock介面定義了鎖的行為
public interface Lock {
//上鎖(不響應Thread.interrupt()直到獲取鎖)
void lock();
//上鎖(響應Thread.interrupt())
void lockInterruptibly() throws InterruptedException;
//嘗試獲取鎖(以nonFair方式獲取鎖)
boolean tryLock();
//在指定時間內嘗試獲取鎖(響應Thread.interrupt(),支援公平/二階段非公平)
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//解鎖
void unlock();
//獲取Condition
Condition newCondition();
}
複製程式碼
lock()過程
//鎖具體實現
private final Sync sync;
//根據傳入引數選擇FairSync或NonfairSync實現
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
#java.util.concurrent.locks.ReentrantLock.Sync
abstract void lock();
複製程式碼
公平鎖
加入同步佇列(當同步佇列為空時會直接獲得鎖),等待鎖
#java.util.concurrent.locks.ReentrantLock.FairSync
final void lock() {
acquire(1);
}
#java.util.concurrent.locks.AbstractQueuedSynchronizer
public final void acquire(int arg) {
if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
複製程式碼
acquire()流程:
-
tryAcquire():模板方法,獲取鎖
#java.util.concurrent.locks.ReentrantLock.FairSync protected final boolean tryAcquire(int acquires) { //獲取當前執行緒 final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) {//當前鎖沒被佔用 if (!hasQueuedPredecessors() &&//1.判斷同步佇列中是否有節點在等待 compareAndSetState(0, acquires)) {//2.如果上面!1成立,修改state值(表明當前鎖已被佔用) setExclusiveOwnerThread(current);//3.如果2成立,修改當前佔用鎖的執行緒為當前執行緒 return true; } } else if (current == getExclusiveOwnerThread()) {//佔用鎖執行緒==當前執行緒(重入) int nextc = c + acquires;// if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc);//修改status return true; } return false;//直接獲取鎖失敗 } 複製程式碼
-
acquireQueued(addWaiter(Node.EXCLUSIVE), arg):加入同步佇列
#java.util.concurrent.locks.AbstractQueuedSynchronizer //1 private Node addWaiter(Node mode) { //生成node Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { //將node加到佇列尾部 node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } //如果加入失敗(多執行緒競爭或者tail指標為null) enq(node); return node; } //1.1 private Node enq(final Node node) { //死迴圈加入節點(cas會失敗) for (;;) { Node t = tail; if (t == null) { //tail為null,同步佇列初始化 //設定head指標 if (compareAndSetHead(new Node()))//注意這裡是個空節點!! tail = head;//將tail也指向head } else { node.prev = t;//將當前node加到隊尾 if (compareAndSetTail(t, node)) { t.next = node; return t;//注意這裡才返回 } } } } //2 final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { //表示是否被打斷 boolean interrupted = false; for (;;) { //獲取node.pre節點 final Node p = node.predecessor(); if (p == head //當前節點是否是同步佇列中的第二個節點 && tryAcquire(arg)) {//獲取鎖,head指向當前節點 setHead(node);//head=head.next p.next = null;//置空 failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && //是否空轉(因為空轉喚醒是個耗時操作,進入空轉前判斷pre節點狀態.如果pre節點即將釋放鎖,則不進入空轉) parkAndCheckInterrupt())//利用unsafe.park()進行空轉(阻塞) interrupted = true;//如果Thread.interrupt()被呼叫,(不會真的被打斷,會繼續迴圈空轉直到獲取到鎖) } } finally { if (failed)//tryAcquire()過程出現異常導致獲取鎖失敗,則移除當前節點 cancelAcquire(node); } } 複製程式碼
過程總結:
- 空轉(如果當前節點是同步佇列中的第二個節點,則直接獲得鎖返回)
- 獲得鎖
注意:這裡有兩次tryAcquire()過程.第一次,為了避免同步佇列為空時還插入佇列產生的效能耗費(cas空轉).第二次,就是正常的流程.先插入隊尾,然後等待喚醒,再獲取鎖
-
selfInterrupt(): 喚醒當前執行緒
static void selfInterrupt() {//在獲取鎖之後 響應intterpt()請求 Thread.currentThread().interrupt(); } 複製程式碼
非公平鎖
一階段
#java.util.concurrent.locks.ReentrantLock.NonfairSync
final void lock() {
//在acquire()之前先嚐試獲取鎖
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
複製程式碼
二階段 acquire()流程與公平鎖一模一樣,唯一區別在於tryAcquire()實現中
#java.util.concurrent.locks.ReentrantLock.NonfairSync
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
#java.util.concurrent.locks.ReentrantLock.Sync
final boolean nonfairTryAcquire(int acquires) {//這個過程其實和FairSync.tryAcquire()基本一致
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//唯一區別: 這裡不會去判斷佇列中是否為空
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;
}
複製程式碼
區別點 | lock()過程(一階段) | tryAcquire()過程(二階段) |
---|---|---|
FairSync | 直接acquire() | 當前若無執行緒持有鎖,如果同步佇列為空,獲取鎖 |
NonFairSync | 先嚐試獲取鎖,再acquire() | 當前若無執行緒持有鎖,獲取鎖 |
unlock()過程
#java.util.concurrent.locks.ReentrantLock
public void unlock() {
sync.release(1);
}
#java.util.concurrent.locks.AbstractQueuedSynchronizer
public final boolean release(int arg) {
if (tryRelease(arg)) {//釋放鎖
Node h = head;
if (h != null &&//head節點為空(非公平鎖直接獲取鎖)
h.waitStatus != 0)
unparkSuccessor(h);//喚醒同步佇列中離head最近的一個waitStatus<=0的節點
return true;
}
return false;
}
#java.util.concurrent.locks.ReentrantLock
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//持有鎖的執行緒==當前執行緒
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//重入鎖全部釋放
free = true;
//置空持有鎖執行緒
setExclusiveOwnerThread(null);
}
//state==0(此時持有鎖,不用cas)
setState(c);
return free;
}
複製程式碼
lockInterruptibly()過程
lockInterruptibly()與lock()過程基本相同,區別在於Thread.intterpt()的應對措施不同
//lock()
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
//表示是否被打斷
boolean interrupted = false;
for (;;) {
//獲取node.pre節點
final Node p = node.predecessor();
if (p == head //當前節點是否是同步佇列中的第二個節點
&& tryAcquire(arg)) {//獲取鎖,當前head指向當前節點
setHead(node);//head=head.next
p.next = null;//置空
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) && //是否空轉(因為空轉喚醒是個耗時操作,進入空轉前判斷pre節點狀態.如果pre節點即將釋放鎖,則不進入空轉)
parkAndCheckInterrupt())//利用unsafe.park()進行空轉(阻塞)
interrupted = true;//如果Thread.interrupt()被呼叫,(不會真的被打斷,會繼續迴圈空轉直到獲取到鎖)
}
} finally {
if (failed)//tryAcquire()過程出現異常導致獲取鎖失敗,則移除當前節點
cancelAcquire(node);
}
}
// lockInterruptibly()
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())//唯一區別當Thread.intterpt()打斷時,直接丟擲異常
throw new InterruptedException();
}
} finally {
if (failed)//然後移除當前節點
cancelAcquire(node);
}
}
複製程式碼
tryLock()
#java.util.concurrent.locks.ReentrantLock
public boolean tryLock() {
//嘗試獲取非公平鎖
return sync.nonfairTryAcquire(1);
}
複製程式碼
tryLock(long timeout, TimeUnit unit)
#java.util.concurrent.locks.ReentrantLock
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
#java.util.concurrent.locks.AbstractQueuedSynchronizer
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||//獲取鎖(公平/非公平)
doAcquireNanos(arg, nanosTimeout);//在指定時間內等待鎖(空轉)
}
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
...
final long deadline = System.nanoTime() + nanosTimeout;
//加入隊尾
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null;
failed = false;
return true;
}
//上面與acquireQueued()相同,重點看這裡
//計算剩餘時間
nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)
return false;
if (shouldParkAfterFailedAcquire(p, node) &&
nanosTimeout > spinForTimeoutThreshold)
//利用parkNanos()指定空轉時間
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())//如果被Thread.interrupt(),則拋異常
throw new InterruptedException();
}
} finally {
if (failed)//移除節點
cancelAcquire(node);
}
}
複製程式碼
newCondition()
public Condition newCondition() {
return sync.newCondition();
}
#java.util.concurrent.locks.ReentrantLock.Sync
final ConditionObject newCondition() {
return new ConditionObject();
}
複製程式碼