1.概念
Condition主要是為了在J.U.C框架中提供和Java傳統(tǒng)的監(jiān)視器風(fēng)格的wait,notify和notifyAll方法類似的功能驳概。?JDK的官方解釋如下:條件(也稱為條件隊(duì)列 或條件變量)為線程提供了一個(gè)含義,以便在某個(gè)狀態(tài)條件現(xiàn)在可能為 true 的另一個(gè)線程通知它之前,一直掛起該線程(即讓其“等待”)。因?yàn)樵L問(wèn)此共享狀態(tài)信息發(fā)生在不同的線程中,所以它必須受保護(hù),因此要將某種形式的鎖與該條件相關(guān)聯(lián)勃痴。等待提供一個(gè)條件的主要屬性是:以原子方式 釋放相關(guān)的鎖,并掛起當(dāng)前線程,就像 Object.wait 做的那樣掌测。Condition實(shí)質(zhì)上是被綁定到一個(gè)鎖上朦前。
2.核心
理解ReentrantLock以及condition的關(guān)鍵是要理解它的內(nèi)部機(jī)制糊肠,其中核心就是:
ReentrantLock的AQS隊(duì)列擂啥、condition隊(duì)列、以及condition.await()方法才避、condition.signal()方法橱夭。
我們需要弄清楚的是:?
? ? ?1.AQS隊(duì)列和codition隊(duì)列之間是什么關(guān)系?桑逝?棘劣?
? ? ?2.condition.await()方法對(duì)condition隊(duì)列以及AQS隊(duì)列的影響?楞遏?
? ? ?3.condition.signal()方法對(duì)condition隊(duì)列以及AQS隊(duì)列的影響茬暇??
? ? 4.AQS隊(duì)列和鎖lock之間的關(guān)系橱健??
3.用代碼驗(yàn)證condition.await()方法
public class TestAwaitMain {
private static ReentrantLock? lock;
public static void main(String[] args) {
// TODO Auto-generated method stub
//1.線程A先持有鎖
//? 然后await
//在B中查詢是否能獲取鎖
//如果能則說(shuō)明 await()方法可以釋放鎖
lock=new ReentrantLock();
Condition condition = lock.newCondition();
//開(kāi)辟兩個(gè)線程
new Thread(new RunnableA(lock,condition),"t_A").start();
try {
? Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new RunnableB(lock,condition),"t_B").start();
lock.lock();
System.out.println("線程"+Thread.currentThread().getName()+"Condition隊(duì)列長(zhǎng)度是? :"+lock.getWaitQueueLength(condition));
System.out.println("等待獲取lock鎖的"+"AQS隊(duì)列長(zhǎng)度是 :"+lock.getQueueLength());
}
}
RunnableA的代碼
public class RunnableA implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableA(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("線程A運(yùn)行");
? ? //先阻塞
lock.lock();
System.out.println("線程"+Thread.currentThread().getName()+"的lock方法已經(jīng)執(zhí)行");
try {
System.out.println("線程"+Thread.currentThread().getName()+"持有鎖嗎 ?:"+lock.isHeldByCurrentThread());
System.out.println("持有鎖的線程數(shù):"+ lock.getHoldCount());
try {
System.out.println("線程"+Thread.currentThread().getName()+"準(zhǔn)備執(zhí)行await()方法");
System.out.println("線程"+Thread.currentThread().getName()+"等待獲取鎖的隊(duì)列長(zhǎng)度"+lock.getQueueLength());
System.out.println("線程"+Thread.currentThread().getName()+"Condition隊(duì)列長(zhǎng)度"+lock.getWaitQueueLength(condition));
lock.hasWaiters(condition);
condition.await();
//此時(shí)沙廉,如果不執(zhí)行await操作
//看看AQS隊(duì)列的長(zhǎng)度
Thread.sleep(10000L);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
//lock.unlock();
}
}
}
RunnableB的代碼
public class RunnableB implements Runnable {
? ? private ReentrantLock lock;
private Condition condition;
public RunnableB(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("線程B運(yùn)行");
? ? //先阻塞
lock.lock();
System.out.println("線程"+Thread.currentThread().getName()+"的lock方法已經(jīng)執(zhí)行");
try {
System.out.println("線程"+Thread.currentThread().getName()+"持有鎖嗎 ?:"+lock.isHeldByCurrentThread());
System.out.println("持有鎖的線程數(shù):"+ lock.getHoldCount());
try {
System.out.println("線程"+Thread.currentThread().getName()+"準(zhǔn)備執(zhí)行await()方法");
System.out.println("線程"+Thread.currentThread().getName()+"等待獲取鎖的AQS隊(duì)列長(zhǎng)度是 :"+lock.getQueueLength());
System.out.println("線程"+Thread.currentThread().getName()+"Condition隊(duì)列長(zhǎng)度是? "+lock.getWaitQueueLength(condition));
//lock.hasWaiters(condition);
condition.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
lock.unlock();
}
}
}
運(yùn)行結(jié)果
分析:
?可見(jiàn)拘荡,在調(diào)用condition.await()方法時(shí),其執(zhí)行流程是這樣的:
? ? 1.釋放當(dāng)前線程持有的鎖
? ? 2.把當(dāng)前線程加入到condition隊(duì)列的尾部撬陵,此時(shí)珊皿,condition的隊(duì)列長(zhǎng)度增加1。
? ? 3.當(dāng)前線程會(huì)在await()方法中不斷嘗試自旋獲取鎖巨税。如果獲取不到鎖蟋定,那么線程A就無(wú)法從await()方法中返回。
4.用代碼驗(yàn)證condition.signal()方法
/**
*
* 驗(yàn)證signal的作用:
*
*? ? 1.線程A先lock
*? ? 2.線程A再await
*? ? 3.線程B執(zhí)行l(wèi)ock
*? ? 4.線程B執(zhí)行signal操作
*? ? 5.此時(shí)草添,檢驗(yàn)線程Bsignal之后驶兜,線程A的await后面的代碼能執(zhí)行嗎??抄淑?
*? ?
*? ? //如果線程B只signal而不釋放鎖的話屠凶,此時(shí)線程A能從await中醒來(lái)嗎?肆资?矗愧??
* @author chihaojie
*
*/
public class TestSignalMain {
private static ReentrantLock? lock;
public static void main(String[] args) {
// TODO Auto-generated method stub
//1.線程A先持有鎖
//? 然后await
//在B中查詢是否能獲取鎖
//如果能則說(shuō)明 await()方法可以釋放鎖
lock=new ReentrantLock();
Condition condition = lock.newCondition();
//開(kāi)辟兩個(gè)線程
new Thread(new RunnableA(lock,condition),"t_A").start();
try {
? Thread.sleep(1000L);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new RunnableB(lock,condition),"t_B").start();
}
}
RunnableA的代碼
public class RunnableA implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableA(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("線程A運(yùn)行");
? ? //先阻塞
lock.lock();
System.out.println("線程"+Thread.currentThread().getName()+"的lock方法已經(jīng)執(zhí)行");
try {
System.out.println("線程"+Thread.currentThread().getName()+"持有鎖嗎 ?:"+lock.isHeldByCurrentThread());
System.out.println("持有鎖的線程數(shù):"+ lock.getHoldCount());
try {
System.out.println("線程"+Thread.currentThread().getName()+"準(zhǔn)備執(zhí)行await()方法");
System.out.println("線程"+Thread.currentThread().getName()+"等待獲取鎖的隊(duì)列長(zhǎng)度"+lock.getQueueLength());
System.out.println("線程"+Thread.currentThread().getName()+"的Condition隊(duì)列長(zhǎng)度"+lock.getWaitQueueLength(condition));
condition.await();
System.out.println("線程"+Thread.currentThread().getName()+"的await被signal之后,持有鎖嗎 ?:"+lock.isHeldByCurrentThread());
//signal之后:
//condition隊(duì)列長(zhǎng)度為0郑原,AQS隊(duì)列長(zhǎng)度為1
System.out.println("線程"+Thread.currentThread().getName()+"的await被signal之后:"+"AQS隊(duì)列長(zhǎng)度是 :"+lock.getQueueLength());
System.out.println("線程"+Thread.currentThread().getName()+"的await被signal之后:"+"Condition隊(duì)列長(zhǎng)度是 :"+lock.getWaitQueueLength(condition));
//此時(shí)唉韭,如果不執(zhí)行await操作
//看看AQS隊(duì)列的長(zhǎng)度
//醒來(lái)之前的必須先獲取鎖
//That thread must then re-acquire the lock before returning from await.
Thread.sleep(10000L);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
lock.unlock();
}
}
}
RunnableB的代碼
public class RunnableB implements Runnable {
private ReentrantLock lock;
private Condition condition;
public RunnableB(ReentrantLock lock, Condition condition) {
super();
this.lock = lock;
this.condition = condition;
}
@Override
public void run() {
System.out.println("線程B運(yùn)行");
? ? //先阻塞
lock.lock();
System.out.println("線程"+Thread.currentThread().getName()+"的lock方法已經(jīng)執(zhí)行");
try {
System.out.println("線程"+Thread.currentThread().getName()+"持有鎖嗎 ?:"+lock.isHeldByCurrentThread());
try {
System.out.println("線程"+Thread.currentThread().getName()+"等待獲取鎖的AQS隊(duì)列長(zhǎng)度是 :"+lock.getQueueLength());
System.out.println("線程"+Thread.currentThread().getName()+"Condition隊(duì)列長(zhǎng)度是? "+lock.getWaitQueueLength(condition));
//lock.hasWaiters(condition);
condition.signal();
//線程B再執(zhí)行了signal()方法之后,我們看一下此時(shí):AQS隊(duì)列的情況以及condition隊(duì)列的情況
System.out.println("線程"+Thread.currentThread().getName()+"執(zhí)行signal()方法之后等待獲取鎖的AQS隊(duì)列長(zhǎng)度是 :"+lock.getQueueLength());
System.out.println("線程"+Thread.currentThread().getName()+"執(zhí)行signal()方法之后Condition隊(duì)列長(zhǎng)度是? "+lock.getWaitQueueLength(condition));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} finally {
// TODO: handle finally clause
//V2:注釋掉lock.unlock();
//即: 如果線程B不釋放鎖的話,即使線程B調(diào)用了signal()方法犯犁,那么線程A能從await方法返回嗎属愤??栖秕?
//V3
lock.unlock();
}
}
}
運(yùn)行結(jié)果:
分析結(jié)果:
從上面的運(yùn)行結(jié)果春塌,我們可以得知:
signal()方法都做了哪些事情?簇捍?只壳?
signal()方法:
? 1.將condition隊(duì)列的頭節(jié)點(diǎn),從condition隊(duì)列中移除
? 2.把上面移除的節(jié)點(diǎn)加入到AQS隊(duì)列的尾部
? 3.讓其等待再次獲取鎖暑塑。
可見(jiàn)吼句,當(dāng)線程B執(zhí)行了signal()之后,AQS隊(duì)列的長(zhǎng)度增加事格,condition隊(duì)列的長(zhǎng)度減少惕艳。并且線程A會(huì)一直在await()方法中嘗試自旋獲取鎖
如果獲取不到鎖,那么線程A就無(wú)法從await()方法中返回驹愚。
5.附上一張?jiān)韴D远搪,方便大家理解
6.補(bǔ)充
對(duì)于lock.lock()方法的理解:
我們先來(lái)看一下逢捺,下面的這段代碼:
try {
? ? ? ? ? ? lock.lock();
? ? ? ? ? ? if (storage > 0) {
? ? ? ? ? ? ? ? putCondition.await();
? ? ? ? ? ? }
? ? ? ? ? ? storage++;
? ? ? ? ? ? System.out.println("put => " + ++putCounter );
? ? ? ? ? ? getCondition.signal();
? ? ? ? } finally {
? ? ? ? ? ? lock.unlock();
? ? ? ? }
我們要知道
ReentrantLock是一個(gè)獨(dú)占式的鎖lock.lcck();
1.lock.lock()這是獲取鎖的操作
2.但是這把鎖有可能已經(jīng)被占用了
3.如果獲取鎖成功谁鳍,即線程繼續(xù)往下執(zhí)行
4.如果鎖已經(jīng)被其他線程獲取了,
5.則該線程會(huì)被阻塞住劫瞳,然后加入到AQS隊(duì)列中倘潜。
即: lock.lock()方法如果獲取不到鎖的話,就會(huì)把當(dāng)前線程加入到AQS隊(duì)列的尾部志于。