這篇文章主要為大家展示了“Java并發(fā)之條件阻塞Condition的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Java并發(fā)之條件阻塞Condition的示例分析”這篇文章吧。
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計、網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、集團企業(yè)網(wǎng)站建設(shè)等服務(wù)項目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了當(dāng)雄免費建站歡迎大家使用!
具體如下。
Condition將Object監(jiān)視器方法(wait、notify 和 notifyAll)分解成截然不同的對象,以便通過將這些對象與任意Lock實現(xiàn)組合使用,為每個對象提供多個等待 set(wait-set)。其中,Lock 替代了synchronized方法和語句的使用,Condition替代了Object監(jiān)視器方法的使用。
由于Condition可以用來替代wait、notify等方法,所以可以對比著之前寫過的線程間通信的代碼來看,再來看一下原來那個問題:
有兩個線程,子線程先執(zhí)行10次,然后主線程執(zhí)行5次,然后再切換到子線程執(zhí)行10,再主線程執(zhí)行5次……如此往返執(zhí)行50次。
之前用wait和notify來實現(xiàn)的,現(xiàn)在用Condition來改寫一下,代碼如下:
public class ConditionCommunication { public static void main(String[] args) { Business bussiness = new Business(); new Thread(new Runnable() { // 開啟一個子線程 @Override public void run() { for (int i = 1; i <= 50; i++) { bussiness.sub(i); } } } ).start(); // main方法主線程 for (int i = 1; i <= 50; i++) { bussiness.main(i); } } } class Business { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); //Condition是在具體的lock之上的 private Boolean bShouldSub = true; public void sub(int i) { lock.lock(); try { while (!bShouldSub) { try { condition.await(); //用condition來調(diào)用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("sub thread sequence of " + j + ", loop of " + i); } bShouldSub = false; condition.signal(); //用condition來發(fā)出喚醒信號,喚醒某一個 } finally { lock.unlock(); } } public void main(int i) { lock.lock(); try { while (bShouldSub) { try { condition.await(); //用condition來調(diào)用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } bShouldSub = true; condition.signal(); //用condition來發(fā)出喚醒信號么,喚醒某一個 } finally { lock.unlock(); } } }
從代碼來看,Condition的使用時和Lock一起的,沒有Lock就沒法使用Condition,因為Condition是通過Lock來new出來的,這種用法很簡單,只要掌握了synchronized和wait、notify的使用,完全可以掌握Lock和Condition的使用。
上面使用Lock和Condition來代替synchronized和Object監(jiān)視器方法實現(xiàn)了兩個線程之間的通信,現(xiàn)在再來寫個稍微高級點應(yīng)用:模擬緩沖區(qū)的阻塞隊列。
什么叫緩沖區(qū)呢?舉個例子,現(xiàn)在有很多人要發(fā)消息,我是中轉(zhuǎn)站,我要幫別人把消息發(fā)出去,那么現(xiàn)在我 就需要做兩件事,一件事是接收用戶發(fā)過來的消息,并按順序放到緩沖區(qū),另一件事是從緩沖區(qū)中按順序取出用戶發(fā)過來的消息,并發(fā)送出去。
現(xiàn)在把這個實際的問題抽象一下:緩沖區(qū)即一個數(shù)組,我們可以向數(shù)組中寫入數(shù)據(jù),也可以從數(shù)組中把數(shù)據(jù)取走,我要做的兩件事就是開啟兩個線程,一個存數(shù)據(jù),一個取數(shù)據(jù)。但是問題來了,如果緩沖區(qū)滿了,說明接收的消息太多了,即發(fā)送過來的消息太快了,我另一個線程還來不及發(fā)完,導(dǎo)致現(xiàn)在緩沖區(qū)沒地方放了,那么此時就得阻塞存數(shù)據(jù)這個線程,讓其等待;相反,如果我轉(zhuǎn)發(fā)的太快,現(xiàn)在緩沖區(qū)所有內(nèi)容都被我發(fā)完了,還沒有用戶發(fā)新的消息來,那么此時就得阻塞取數(shù)據(jù)這個線程。
好了,分析完了這個緩沖區(qū)的阻塞隊列,下面就用Condition技術(shù)來實現(xiàn)一下:
class Buffer { final Lock lock = new ReentrantLock(); //定義一個鎖 final Condition notFull = lock.newCondition(); //定義阻塞隊列滿了的Condition final Condition notEmpty = lock.newCondition(); //定義阻塞隊列空了的Condition final Object[] items = new Object[10]; //為了下面模擬,設(shè)置阻塞隊列的大小為10,不要設(shè)太大 int putptr, takeptr, count; //數(shù)組下標(biāo),用來標(biāo)定位置的 //往隊列中存數(shù)據(jù) public void put(Object x) throws InterruptedException { lock.lock(); //上鎖 try { while (count == items.length) { System.out.println(Thread.currentThread().getName() + " 被阻塞了,暫時無法存數(shù)據(jù)!"); notFull.await(); //如果隊列滿了,那么阻塞存數(shù)據(jù)這個線程,等待被喚醒 } //如果沒滿,按順序往數(shù)組中存 items[putptr] = x; if (++putptr == items.length) //這是到達數(shù)組末端的判斷,如果到了,再回到始端 putptr = 0; ++count; //消息數(shù)量 System.out.println(Thread.currentThread().getName() + " 存好了值: " + x); notEmpty.signal(); //好了,現(xiàn)在隊列中有數(shù)據(jù)了,喚醒隊列空的那個線程,可以取數(shù)據(jù)啦 } finally { lock.unlock(); //放鎖 } } //從隊列中取數(shù)據(jù) public Object take() throws InterruptedException { lock.lock(); //上鎖 try { while (count == 0) { System.out.println(Thread.currentThread().getName() + " 被阻塞了,暫時無法取數(shù)據(jù)!"); notEmpty.await(); //如果隊列是空,那么阻塞取數(shù)據(jù)這個線程,等待被喚醒 } //如果沒空,按順序從數(shù)組中取 Object x = items[takeptr]; if (++takeptr == items.length) //判斷是否到達末端,如果到了,再回到始端 takeptr = 0; --count; //消息數(shù)量 System.out.println(Thread.currentThread().getName() + " 取出了值: " + x); notFull.signal(); //好了,現(xiàn)在隊列中有位置了,喚醒隊列滿的那個線程,可以存數(shù)據(jù)啦 return x; } finally { lock.unlock(); //放鎖 } } }
這個程序很經(jīng)典,我從官方JDK文檔中拿出來的,然后加了注釋。程序中定義了兩個Condition,分別針對兩個線程,等待和喚醒分別用不同的Condition來執(zhí)行,思路很清晰,程序也很健壯??梢钥紤]一個問題,為啥要用兩個Codition呢?之所以這么設(shè)計肯定是有原因的,如果用一個Condition,現(xiàn)在假設(shè)隊列滿了,但是有2個線程A和B同時存數(shù)據(jù),那么都進入了睡眠,好,現(xiàn)在另一個線程取走一個了,然后喚醒了其中一個線程A,那么A可以存了,存完后,A又喚醒一個線程,如果B被喚醒了,那就出問題了,因為此時隊列是滿的,B不能存的,B存的話就會覆蓋原來還沒被取走的值,就因為使用了一個Condition,存和取都用這個Condition來睡眠和喚醒,就亂了套。到這里,就能體會到這個Condition的用武之地了,現(xiàn)在來測試一下上面的阻塞隊列的效果:
public class BoundedBuffer { public static void main(String[] args) { Buffer buffer = new Buffer(); for (int i = 0; i < 5; i ++) { //開啟5個線程往緩沖區(qū)存數(shù)據(jù) new Thread(new Runnable() { @Override public void run() { try { buffer.put(new Random().nextint(1000)); //隨機存數(shù)據(jù) } catch (InterruptedException e) { e.printStackTrace(); } } } ).start(); } for (int i = 0; i < 10; i ++) { //開啟10個線程從緩沖區(qū)中取數(shù)據(jù) new Thread(new Runnable() { @Override public void run() { try { buffer.take(); //從緩沖區(qū)取數(shù)據(jù) } catch (InterruptedException e) { e.printStackTrace(); } } } ).start(); } } }
我故意只開啟5個線程存數(shù)據(jù),10個線程取數(shù)據(jù),就是想讓它出現(xiàn)取數(shù)據(jù)被阻塞的情況發(fā)生,看運行的結(jié)果:
Thread-5 被阻塞了,暫時無法取數(shù)據(jù)!
Thread-10 被阻塞了,暫時無法取數(shù)據(jù)!
Thread-1 存好了值: 755
Thread-0 存好了值: 206
Thread-2 存好了值: 741
Thread-3 存好了值: 381
Thread-14 取出了值: 755
Thread-4 存好了值: 783
Thread-6 取出了值: 206
Thread-7 取出了值: 741
Thread-8 取出了值: 381
Thread-9 取出了值: 783
Thread-5 被阻塞了,暫時無法取數(shù)據(jù)!
Thread-11 被阻塞了,暫時無法取數(shù)據(jù)!
Thread-12 被阻塞了,暫時無法取數(shù)據(jù)!
Thread-10 被阻塞了,暫時無法取數(shù)據(jù)!
Thread-13 被阻塞了,暫時無法取數(shù)據(jù)!
從結(jié)果中可以看出,線程5和10搶先執(zhí)行,發(fā)現(xiàn)隊列中沒有,于是就被阻塞了,睡在那了,直到隊列中有新的值存入才可以取,但是它們兩運氣不好,存的數(shù)據(jù)又被其他線程給搶先取走了,哈哈……可以多運行幾次。如果想要看到存數(shù)據(jù)被阻塞,可以將取數(shù)據(jù)的線程設(shè)置少一點,這里我就不設(shè)了。
還是原來那個題目,現(xiàn)在讓三個線程來執(zhí)行,看一下題目:
有三個線程,子線程1先執(zhí)行10次,然后子線程2執(zhí)行10次,然后主線程執(zhí)行5次,然后再切換到子線程1執(zhí)行10次,子線程2執(zhí)行10次,主線程執(zhí)行5次……如此往返執(zhí)行50次。
如過不用Condition,還真不好弄,但是用Condition來做的話,就非常方便了,原理很簡單,定義三個Condition,子線程1執(zhí)行完喚醒子線程2,子線程2執(zhí)行完喚醒主線程,主線程執(zhí)行完喚醒子線程1。喚醒機制和上面那個緩沖區(qū)道理差不多,下面看看代碼吧,很容易理解。
public class ThreeConditionCommunication { public static void main(String[] args) { Business bussiness = new Business(); new Thread(new Runnable() { // 開啟一個子線程 @Override public void run() { for (int i = 1; i <= 50; i++) { bussiness.sub1(i); } } } ).start(); new Thread(new Runnable() { // 開啟另一個子線程 @Override public void run() { for (int i = 1; i <= 50; i++) { bussiness.sub2(i); } } } ).start(); // main方法主線程 for (int i = 1; i <= 50; i++) { bussiness.main(i); } } static class Business { Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition(); //Condition是在具體的lock之上的 Condition condition2 = lock.newCondition(); Condition conditionMain = lock.newCondition(); private int bShouldSub = 0; public void sub1(int i) { lock.lock(); try { while (bShouldSub != 0) { try { condition1.await(); //用condition來調(diào)用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("sub1 thread sequence of " + j + ", loop of " + i); } bShouldSub = 1; condition2.signal(); //讓線程2執(zhí)行 } finally { lock.unlock(); } } public void sub2(int i) { lock.lock(); try { while (bShouldSub != 1) { try { condition2.await(); //用condition來調(diào)用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 10; j++) { System.out.println("sub2 thread sequence of " + j + ", loop of " + i); } bShouldSub = 2; conditionMain.signal(); //讓主線程執(zhí)行 } finally { lock.unlock(); } } public void main(int i) { lock.lock(); try { while (bShouldSub != 2) { try { conditionMain.await(); //用condition來調(diào)用await方法 } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (int j = 1; j <= 5; j++) { System.out.println("main thread sequence of " + j + ", loop of " + i); } bShouldSub = 0; condition1.signal(); //讓線程1執(zhí)行 } finally { lock.unlock(); } } } }
以上是“Java并發(fā)之條件阻塞Condition的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!