目錄
3、await / awaitUninterruptibly
4、await / awaitNanos / awaitUntil
上一篇《Java8 ReentrantLock 源碼解析》中主要講解了lock和unlock相關方法的實現,本篇講解ReentrantLock的newCondition方法返回的ConditionObject實例的使用和實現細節。
1、定義
ConditionObject是AbstractQueuedSynchronizer的一個內部類,用於實現Condition接口,ReentrantLock通過newCondition方法返回該類的一個實例,如下:
public Condition newCondition() {
return sync.newCondition();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
Condition接口定義的方法如下:
這裏的await相當於Object的wait方法,signal和signalAll方法相當於Object的notify和notifyAll方法。concurrent包下各種阻塞隊列和線程池等就是Condition接口實現阻塞,以await方法的調用鏈爲例說明,如下:
ConditionObject只有兩個屬性:
- private transient Node firstWaiter; //在某個Condition實例上等待的Node鏈表的鏈表頭,即調用了某個condition實例的await方法的等待線程鏈表
- private transient Node lastWaiter; //在某個Condition實例上等待的Node鏈表的鏈表尾
注意上述鏈表是通過Node的nextWaiter構成的單向鏈表,跟通過Node的prev,next屬性構成的等待獲取鎖的同步鏈表是不一樣的,後者的鏈表頭和鏈表爲分別是AbstractQueuedSynchronizer的head和tail屬性。
ConditionObject還定義了兩個常量,描述await方法退出時的處理方式,如下:
REINTERRUPT表示將當前線程的中斷標識重置爲true,因爲之前獲取線程是否中斷時,會將線程的中斷標識給清除掉重置爲false,此處是爲了還原;THROW_IE表示會拋出一個InterruptedException異常,表示當前線程被喚醒是因爲被中斷了。
2、使用
ConditionObject的await系列方法相當於Object的wait方法,signal和signalAll方法分別對應於Object的notify和notifyAll方法,跟Object一樣,執行這些方法前都需要檢查當前線程是否持有鎖,如果沒有持有則會拋出IllegalMonitorStateException異常,ConditionObject的方法和Object的對應方法的實現邏輯也大體一致,參考如下測試用例:
@Test
public void name2() throws Exception {
Object lock = new Object();
Runnable a = new Runnable() {
@Override
public void run() {
try {
System.out.println("run start,time->" + System.currentTimeMillis());
Thread.sleep(6000);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (lock) {
lock.notify();
System.out.println("run end");
}
}
};
Thread thread = new Thread(a);
thread.start();
synchronized (lock) {
System.out.println("main start,time->" + System.currentTimeMillis());
long start = System.currentTimeMillis();
lock.wait(1000);
System.out.println("wait time->" + (System.currentTimeMillis() - start));
}
System.out.println("main end");
}
@Test
public void name3() throws Exception {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Runnable a = new Runnable() {
@Override
public void run() {
lock.lock();
try {
System.out.println("run start,time->" + System.currentTimeMillis());
Thread.sleep(6000);
condition.signal();
System.out.println("run end");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread thread = new Thread(a);
lock.lock();
try {
thread.start();
System.out.println("main start,time->" + System.currentTimeMillis());
long start = System.currentTimeMillis();
condition.await(1, TimeUnit.SECONDS);
System.out.println("wait time->" + (System.currentTimeMillis() - start));
} finally {
lock.unlock();
}
System.out.println("main end");
}
這兩個的執行結果一樣,如下:
main start,time->1585901311285
run start,time->1585901311286
run end
wait time->6001
main end
wait方法明明最多等待1s,爲啥實際等待了6s了,難道是1s後沒有被喚醒?答案是調用await和wait方法都會釋放當前佔有的鎖,然後阻塞當前線程;1s過了,阻塞的線程被喚醒了,因爲await和wait方法後面的邏輯處於加鎖的代碼塊中,所以線程被喚醒後需要重新去搶佔鎖,此時鎖是被線程thread佔用的,所以線程會被喚醒後嘗試獲取失敗又繼續被阻塞了,直到6s後thread線程釋放鎖並喚醒阻塞的main線程,main線程重新獲取鎖成功,才從await或者wait方法退出,執行下面的邏輯。
3、await / awaitUninterruptibly
await方法用於讓當前線程阻塞,直到被喚醒或者被打斷,如果被打斷則拋出異常InterruptedException,awaitUninterruptibly會一直等待直到被喚醒,被打斷時不會拋出異常,可以通過線程的中斷標識判斷是否因爲中斷被喚醒的,注意無論是被signal喚醒的還是被中斷喚醒的,都需要再次獲取鎖纔可以退出await方法。
public final void await() throws InterruptedException {
if (Thread.interrupted())
//如果線程已中斷拋出異常
throw new InterruptedException();
//在ConditionWaiter鏈表末尾插入一個新的狀態是CONDITION的節點
Node node = addConditionWaiter();
//釋放佔有的鎖,並喚醒同步隊列中下一個等待節點,如果當前線程未持有鎖則拋出異常
int savedState = fullyRelease(node);
int interruptMode = 0;
//isOnSyncQueue判斷node是否在等待獲取鎖的同步鏈表中,對於剛創建的ConditionWaiter節點,沒有將其加入到等待獲取鎖的同步鏈表中
//只是將其加入到通過nextWaiter屬性維護的等待鏈表中了,該方法返回false
//等該節點被signal方法喚醒後就會將其加入到同步鏈表中了,該方法返回true
while (!isOnSyncQueue(node)) {
//執行park讓這個線程處於阻塞狀態
LockSupport.park(this);
//線程被喚醒了,如果線程被中斷的話,checkInterruptWhileWaiting的返回值就不是0,此時會退出循環,checkInterruptWhileWaiting在執行時會將當前節點加入到同步鏈表中
//如果不是被中斷,則繼續while循環
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//acquireQueued方法會嘗試獲取鎖,如果失敗則阻塞當前線程直到獲取成功,返回值爲true,表示最後一次喚醒是因爲線程中斷
//interruptMode爲0或者REINTERRUPT
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
//從鏈表中移除所有非CONDITION的節點
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
public final void awaitUninterruptibly() {
//添加一個新的節點
Node node = addConditionWaiter();
//釋放鎖
int savedState = fullyRelease(node);
boolean interrupted = false;
//判斷node是否在同步鏈表中
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
//如果線程被中斷過將interrupted 置爲true,此時會繼續循環,直到被喚醒,node加入到同步鏈表中,while循環退出
if (Thread.interrupted())
interrupted = true;
}
//如果獲取鎖成功或者interrupted爲true,將當前線程標記爲已中斷
if (acquireQueued(node, savedState) || interrupted)
selfInterrupt();
}
//在鏈表末尾插入一個新的狀態是CONDITION的節點,因爲此時未釋放鎖,所以只有一個線程在操作waiter鏈表,不需要通過CAS修改lastWaiter屬性
private Node addConditionWaiter() {
//獲取最後一個節點
Node t = lastWaiter;
//如果t不是CONDITION,則從鏈表中移除所有非CONDITION的節點
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//創建一個新節點
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
//鏈表爲空
firstWaiter = node;
else
//插入到lastWaiter的後面
t.nextWaiter = node;
//重置lastWaiter
lastWaiter = node;
return node;
}
//會遍歷一遍鏈表,清理掉所有不是CONDITION的節點
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
//trail表示上一個非CONDITION節點
Node trail = null;
//遍歷整個鏈表
while (t != null) {
//獲取下一個節點
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
//將t從鏈表中移除
t.nextWaiter = null;
if (trail == null)
//未找到非CONDITION節點,即之前遍歷的節點都被移除了
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
//遍歷到末尾了,將tail置爲lastWaiter
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
final int fullyRelease(Node node) {
boolean failed = true;
try {
//獲取當前的同步器狀態
int savedState = getState();
//release方法返回true,表示成功釋放鎖,返回false表示不能釋放鎖
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
//如果release方法返回false進入此分支,將狀態置爲CANCELLED
node.waitStatus = Node.CANCELLED;
}
}
protected final int getState() {
return state;
}
public final boolean release(int arg) {
//tryRelease是子類實現的方法,會校驗當前線程是否持有鎖,如果沒有則拋出IllegalMonitorStateException異常,如果佔有了則用state減去arge,如果等於0返回true,需要釋放鎖
if (tryRelease(arg)) {
//獲取鏈表頭
Node h = head;
//waitStatus初始值是0
if (h != null && h.waitStatus != 0)
//喚醒下一個不是CANCELLED的節點對應的線程
unparkSuccessor(h);
return true;
}
return false;
}
//isOnSyncQueue判斷node是否在等待獲取鎖的同步鏈表中
final boolean isOnSyncQueue(Node node) {
//如果是CONDITION則需要從鏈表中移除
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
//prev肯定不爲空,但是next可能爲空,比如該節點就是末尾節點
return findNodeFromTail(node);
}
//從tail開始往前遍歷,判斷目標節點是否在鏈表中
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
final boolean transferAfterCancelledWait(Node node) {
//如果狀態是CONDITION,將其原子的置爲0
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
//如果修改成功,將其加入到同步隊列中,注意此時node還在等待鏈表中
enq(node);
return true;
}
//如果修改失敗,說明其他某個線程修改了狀態(另一個線程同時喚醒了此節點),則通過isOnSyncQueue方法判斷其
//是否在同步隊列中,如果不在則執行yeild,不斷while循環,直到該方法返回true
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
private Node enq(final Node node) {
//for循環直到原子修改成功爲止
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
//原子的設置head
tail = head;
} else {
//tail不爲空,將新節點插入到tail的後面
node.prev = t;
if (compareAndSetTail(t, node)) {
//如果原子的修改成功
t.next = node;
return t;
}
}
}
}
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
//拋出異常
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
//重新中斷當前線程
selfInterrupt();
}
測試用例如下:
@Test
public void test3() throws Exception {
ReentrantLock lock=new ReentrantLock();
Condition condition=lock.newCondition();
Thread a=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
condition.await();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
a.start();
//讓a線程執行起來
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(5000);
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
b.start();
//讓b線程執行起來
Thread.sleep(100);
int i=0;
long start=System.currentTimeMillis();
while (a.isAlive()){
a.interrupt();
i++;
Thread.sleep(10);
}
System.out.println("main thread end,i->"+i+",time->"+(System.currentTimeMillis()-start));
}
執行結果如下:
在await方法退出前一共調用interrupt方法489次,從開始調用到await方法退出總耗時4904s,第一次調用interrupt方法會讓當前線程從while循環中退出,然後進入到acquireQueued方法嘗試獲取鎖,因爲此時線程b在休眠過程中未釋放鎖,所以進入到acquireQueued方法後會被阻塞,後面每次調用interrupt方法都會喚醒線程然後重新嘗試獲取鎖,直到獲取成功爲止,從acquireQueued方法退出。退出後因爲interruptMode是THROW_IE,然後拋出InterruptedException異常。耗時4904ms是因爲執行到start時,線程b實際已經運行了100ms左右,再過4900ms左右,就會執行signal喚醒在condition上等待的線程,線程a獲取鎖從await方法退出,main線程的while循環終止並退出。
將上述用例換成synchronized關鍵字,效果是一樣的,如下:
@Test
public void test4() throws Exception {
Object lock=new Object();
Thread a=new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (lock) {
lock.wait();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
a.start();
//讓a線程執行起來
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (lock) {
Thread.sleep(5000);
lock.notify();
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
b.start();
//讓b線程執行起來
Thread.sleep(100);
int i=0;
long start=System.currentTimeMillis();
while (a.isAlive()){
a.interrupt();
i++;
Thread.sleep(10);
}
System.out.println("main thread end,i->"+i+",time->"+(System.currentTimeMillis()-start));
}
如果換成awaitUninterruptibly方法,則不會拋出異常,此時可以通過線程的中斷標識來判斷是否是因爲被中斷喚醒的,如下:
@Test
public void test3() throws Exception {
ReentrantLock lock=new ReentrantLock();
Condition condition=lock.newCondition();
Thread a=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
condition.awaitUninterruptibly();
System.out.println("interrupted->"+Thread.currentThread().isInterrupted());
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
a.start();
//讓a線程執行起來
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(5000);
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
b.start();
//讓b線程執行起來
Thread.sleep(100);
int i=0;
long start=System.currentTimeMillis();
while (a.isAlive()){
a.interrupt();
i++;
Thread.sleep(10);
}
System.out.println("main thread end,i->"+i+",time->"+(System.currentTimeMillis()-start));
}
測試結果如下:
4、await / awaitNanos / awaitUntil
上面的await是沒有時間參數的,帶時間參數的await方法有三個版本,await方法被中斷喚醒後會嘗試獲取鎖並拋出異常,如果是被signal喚醒的,則返回等待時間是否超時了,返回false表示等待超時了,true表示沒有被超時。awaitNanos的實現跟await基本一致,就返回值不同,awaitNanos返回剩餘的等待時間,如果爲負值,表示等待超時了,否則未超時。awaitUntil的實現及返回值都和await一致,但是入參不同,await傳遞的是相對時間,從線程開始休眠算起,awaitUntil傳遞的是絕對時間,即具體的某一個時間點。
//返回是否因等待超時被喚醒,false表示等待超時了,true表示沒有等待超時
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
//如果線程中斷,則拋出異常
throw new InterruptedException();
//添加一個ConditionWaiter節點
Node node = addConditionWaiter();
//釋放鎖
int savedState = fullyRelease(node);
//計算等待的最遲期限
final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
//判斷node是否在同步等待隊列中
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
//等待的時間到了
timedout = transferAfterCancelledWait(node);
break;
}
//等待的時間較長,將線程park
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
//線程被喚醒,如果線程被中斷則checkInterruptWhileWaiting返回1或者-1,終止循環
//如果是被signal方法喚醒則檢查node是否在同步鏈表中,如果在則退出循環,嘗試獲取鎖
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
//線程是被signal方法喚醒的,重新計算等待的時間
nanosTimeout = deadline - System.nanoTime();
}
//這部分邏輯跟不帶參數的await方法一致
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
//awaitNanos的邏輯和帶時間參數的await方法實現一致,就是返回值不同,返回剩餘的等待時間
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return deadline - System.nanoTime();
}
//跟上面兩個相比就是等待的時間不一致,上面兩個是相對時間,從進入方法開始算起的,awaitUntil是絕對時間,即一個固定的時間點
public final boolean awaitUntil(Date deadline)
throws InterruptedException {
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (System.currentTimeMillis() > abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
//abstime是絕對時間
LockSupport.parkUntil(this, abstime);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
return !timedout;
}
測試用例如下:
@Test
public void test3() throws Exception {
ReentrantLock lock=new ReentrantLock();
Condition condition=lock.newCondition();
CountDownLatch countDownLatch=new CountDownLatch(1);
Thread a=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
// boolean result=condition.await(6, TimeUnit.SECONDS);
// long result=condition.awaitNanos(TimeUnit.SECONDS.toNanos(4));
Calendar calendar= Calendar.getInstance();
calendar.add(Calendar.SECOND,5);
boolean result=condition.awaitUntil(calendar.getTime());
System.out.println("await result->"+result);
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
a.start();
//讓a線程執行起來
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(5000);
condition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
b.start();
//讓b線程執行起來
Thread.sleep(100);
long start=System.currentTimeMillis();
countDownLatch.await();
System.out.println("main thread end,time->"+(System.currentTimeMillis()-start));
}
5、signal / signalAll
signal和signalAll的核心都是transferForSignal方法,區別在於前者只處理firstWaiter一個節點,而signalAll會處理所有的節點。transferForSignal方法並不會直接喚醒等待中的線程,而是將其加入到等待獲取鎖的同步鏈表的後面,然後將該節點的前一個節點的狀態改成SIGNAL,表示需要喚醒該節點。等同步鏈表前面的節點都獲取鎖並釋放鎖後,纔會讓該節點獲取鎖。
public final void signal() {
//如果沒有獲取鎖,則拋出異常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
public final void signalAll() {
//如果沒有獲取鎖,則拋出異常
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && //如果first是一個CANCLE節點,則transferForSignal返回false,然後通過while循環處理下一個節點
//否則返回true,終止循環
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
//將node的狀態由CONDITION修改成0,如果修改失敗,說明不是CONDITION而是CANCLE
//即node是一個無效節點
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
//將node插入到同步鏈表的末尾,返回鏈表中node的上一個節點,即插入前的tail節點
Node p = enq(node);
int ws = p.waitStatus;
//如果上一個節點是CANCLE,說明當前互斥鎖未被佔用,可喚醒當前線程獲取鎖
//將上一個tail節點狀態爲SIGNAL失敗,說明該節點就是head節點,且head節點準備釋放鎖將狀態置爲0
//正常情形就是將p的狀態修改成SIGNAL,
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
private void doSignalAll(Node first) {
//first本身就是firstWaiter,所以此處可以先將lastWaiter和firstWaiter置爲null
lastWaiter = firstWaiter = null;
do {
//從first開始遍歷所有的節點
Node next = first.nextWaiter;
first.nextWaiter = null;
//將目標節點加入到同步隊列中
transferForSignal(first);
first = next;
} while (first != null);
}
測試用例如下:
@Test
public void test() throws Exception {
ReentrantLock lock=new ReentrantLock();
Condition condition=lock.newCondition();
CountDownLatch countDownLatch=new CountDownLatch(8);
Runnable wait=new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+",start time->"+System.nanoTime());
lock.lock();
condition.await();
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+",await time->"+System.nanoTime());
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
};
for(int i=0;i<5;i++){
Thread thread=new Thread(wait);
thread.start();
}
//讓上面5個線程運行起來並進入到await等待鏈表中
Thread.sleep(2000);
System.out.println("all wait thread start");
Thread a=new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
Thread.sleep(5000);
//根據awaiter隊列的順序,將await的線程加入到同步隊列中
condition.signalAll();
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
a.start();
//讓線程a運行起來,進入sleep
Thread.sleep(100);
//讓線程b和線程c加入到同步隊列中
Thread b=new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+",start time->"+System.nanoTime());
lock.lock();
System.out.println(Thread.currentThread().getName()+",lock time->"+System.nanoTime());
Thread.sleep(1000);
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
b.start();
Thread c=new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+",start time->"+System.nanoTime());
lock.lock();
System.out.println(Thread.currentThread().getName()+",lock time->"+System.nanoTime());
Thread.sleep(1000);
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
});
c.start();
countDownLatch.await();
System.out.println("main thread end");
}
其輸出如下:
#從上到下,5個線程依次啓動,並依次加入到await等待鏈表中
Thread-0,start time->2257452263025112
Thread-4,start time->2257452264363671
Thread-2,start time->2257452264494532
Thread-1,start time->2257452265047514
Thread-3,start time->2257452265350669
all wait thread start
//線程b和c都起來了,進入等待鎖的同步鏈表中
Thread-6,start time->2257454364189121
Thread-7,start time->2257454364855324
//線程b和c依次獲取鎖
Thread-6,lock time->2257459262985730
Thread-7,lock time->2257460262757235
//5個線程按照加入到await等待鏈表中的順序依次被喚醒獲取鎖
Thread-0,await time->2257461362593351
Thread-4,await time->2257461462572635
Thread-2,await time->2257461562551508
Thread-1,await time->2257461662549663
Thread-3,await time->2257461762404238
main thread end
在默認的非公平鎖下,最早加入等待獲取鎖的同步鏈表中的線程優先獲取鎖,當在某個condition實例上等待的線程被喚醒後,是加入到同步鏈表的末尾等待獲取鎖;而synchronized的實現是相反的,最近加入到同步鏈表中的線程優先獲取鎖,當在某個Object實例上等待的線程被喚醒後,是加入到同步鏈表的前面,最近才加入到等待鏈表中線程在同步鏈表中的位置越靠前。測試用例如下:
@Test
public void test2() throws Exception {
Object lock=new Object();
CountDownLatch countDownLatch=new CountDownLatch(8);
Runnable wait=new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+",start time->"+System.nanoTime());
synchronized (lock) {
lock.wait();
Thread.sleep(100);
}
System.out.println(Thread.currentThread().getName()+",await time->"+System.nanoTime());
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
for(int i=0;i<5;i++){
Thread thread=new Thread(wait);
thread.start();
}
Thread.sleep(2000);
System.out.println("all wait thread start");
Thread a=new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (lock) {
Thread.sleep(5000);
lock.notifyAll();
}
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
});
a.start();
Thread.sleep(100);
Thread b=new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+",start time->"+System.nanoTime());
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + ",lock time->" + System.nanoTime());
Thread.sleep(1000);
}
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
});
b.start();
Thread c=new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+",start time->"+System.nanoTime());
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + ",lock time->" + System.nanoTime());
Thread.sleep(1000);
}
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
});
c.start();
countDownLatch.await();
System.out.println("main thread end");
}
執行結果如下:
# 5個線程依次啓動,進入等待鏈表中
Thread-0,start time->2257752210301534
Thread-1,start time->2257752211441136
Thread-4,start time->2257752213288373
Thread-2,start time->2257752213312986
Thread-3,start time->2257752214007084
all wait thread start
//線程 b和c依次啓動,進入同步鏈表中
Thread-6,start time->2257754313978574
Thread-7,start time->2257754314997979
//線程a開始釋放鎖,notifyAll將所有等待鏈表中的節點按照相反的順序加入到同步鏈表的前面
//按照同步鏈表的順序依次喚醒線程,最近加入的優先被喚醒
Thread-3,await time->2257759311780596
Thread-2,await time->2257759411766853
Thread-4,await time->2257759511758033
Thread-1,await time->2257759611688911
Thread-0,await time->2257759711730959
Thread-7,lock time->2257759711767879
Thread-6,lock time->2257760711589432
main thread end
6、其他方法實現總結
ReentrantLock中有多個可以查看Condition等待隊列狀態的方法,其實現比較簡單,這裏總結如下:
- getWaitingThreads:獲取在某個Condition實例上等待的線程列表,從firstWaiter開始往後遍歷,將所有狀態是CONDITION的節點對應的Thread加入到列表中並返回即可。
- getWaitQueueLength:獲取在某個Condition實例上等待的線程的個數,遍歷方式同上,不過此方法是計數累加
- hasWaiters:判斷在某個Condition實例上是否有等待的線程,遍歷方式同上,不過找到一個狀態是CONDITION的節點即返回true