Java使用Condition控制線程通信的方法實(shí)例詳解
本文實(shí)例講述了Java使用Condition控制線程通信的方法。分享給大家供大家參考,具體如下:
一 點(diǎn)睛
當(dāng)使用Lock對(duì)象來保證同步時(shí),Java提供了一個(gè)Condition類來保持協(xié)調(diào),使用Condition可以讓那些已經(jīng)得到Lock對(duì)象、卻無法繼續(xù)執(zhí)行的線程釋放Lock對(duì)象,Condtion對(duì)象也可以喚醒其他處于等待的線程。
Condition 將同步監(jiān)視鎖方法(wait、notify 和 notifyAll)分解成截然不同的對(duì)象,以便通過將這些對(duì)象與Lock對(duì)象組合使用,為每個(gè)對(duì)象提供多個(gè)等待集(wait-set)。在這種情況下,Lock 替代了同步方法或同步代碼塊,Condition替代了同步監(jiān)視鎖的功能。
Condition實(shí)例實(shí)質(zhì)上被綁定在一個(gè)Lock對(duì)象上。要獲得特定Lock實(shí)例的Condition實(shí)例,調(diào)用Lock對(duì)象newCondition()方法即可。Condtion類提供了如下三個(gè)方法:
await():類似于隱式同步監(jiān)視器上的wait()方法,導(dǎo)致當(dāng)前線程等待,直到其他線程調(diào)用該Condtion的signal ()方法或signalAll ()方法來喚醒該線程。該await方法有更多變體:long awaitNanos(long nanosTimeout)、void awaitUninterruptibly()、awaitUntil(Date deadline)等,可以完成更豐富的等待操作。
signal ():?jiǎn)拘言诖薒ock對(duì)象上等待的單個(gè)線程。如果所有線程都在該Lock對(duì)象上等待,則會(huì)選擇喚醒其中一個(gè)線程。選擇是任意性的。只有當(dāng)前線程放棄對(duì)該Lock對(duì)象的鎖定后(使用await()方法),才可以執(zhí)行被喚醒的線程。
signalAll():?jiǎn)拘言诖薒ock對(duì)象上等待的所有線程。只有當(dāng)前線程放棄對(duì)該該Lock對(duì)象的鎖定后,才可以執(zhí)行被喚醒的線程。
二 代碼
1 Account類
public class Account { // 顯式定義Lock對(duì)象 private final Lock lock = new ReentrantLock(); // 獲得指定Lock對(duì)象對(duì)應(yīng)的Condition private final Condition cond = lock.newCondition(); // 封裝賬戶編號(hào)、賬戶余額的兩個(gè)成員變量 private String accountNo; private double balance; // 標(biāo)識(shí)賬戶中是否已有存款的旗標(biāo) private boolean flag = false; public Account(){} // 構(gòu)造器 public Account(String accountNo , double balance) { this.accountNo = accountNo; this.balance = balance; } // accountNo的setter和getter方法 public void setAccountNo(String accountNo) { this.accountNo = accountNo; } public String getAccountNo() { return this.accountNo; } // 因此賬戶余額不允許隨便修改,所以只為balance提供getter方法, public double getBalance() { return this.balance; } public void draw(double drawAmount) { // 加鎖 lock.lock(); try { // 如果flag為假,表明賬戶中還沒有人存錢進(jìn)去,取錢方法阻塞 if (!flag) { cond.await(); } else { // 執(zhí)行取錢 System.out.println(Thread.currentThread().getName() + " 取錢:" + drawAmount); balance -= drawAmount; System.out.println("賬戶余額為:" + balance); // 將標(biāo)識(shí)賬戶是否已有存款的旗標(biāo)設(shè)為false。 flag = false; // 喚醒其他線程 cond.signalAll(); } } catch (InterruptedException ex) { ex.printStackTrace(); } // 使用finally塊來釋放鎖 finally { lock.unlock(); } } public void deposit(double depositAmount) { lock.lock(); try { // 如果flag為真,表明賬戶中已有人存錢進(jìn)去,則存錢方法阻塞 if (flag) // ① { cond.await(); } else { // 執(zhí)行存款 System.out.println(Thread.currentThread().getName() + " 存款:" + depositAmount); balance += depositAmount; System.out.println("賬戶余額為:" + balance); // 將表示賬戶是否已有存款的旗標(biāo)設(shè)為true flag = true; // 喚醒其他線程 cond.signalAll(); } } catch (InterruptedException ex) { ex.printStackTrace(); } // 使用finally塊來釋放鎖 finally { lock.unlock(); } } // 下面兩個(gè)方法根據(jù)accountNo來重寫hashCode()和equals()方法 public int hashCode() { return accountNo.hashCode(); } public boolean equals(Object obj) { if(this == obj) return true; if (obj !=null && obj.getClass() == Account.class) { Account target = (Account)obj; return target.getAccountNo().equals(accountNo); } return false; } }
2 DrawThread線程類
public class DrawThread extends Thread { // 模擬用戶賬戶 private Account account; // 當(dāng)前取錢線程所希望取的錢數(shù) private double drawAmount; public DrawThread(String name , Account account , double drawAmount) { super(name); this.account = account; this.drawAmount = drawAmount; } // 重復(fù)100次執(zhí)行取錢操作 public void run() { for (int i = 0 ; i < 100 ; i++ ) { account.draw(drawAmount); } } }
3 DepositThread線程類
public class DepositThread extends Thread { // 模擬用戶賬戶 private Account account; // 當(dāng)前取錢線程所希望存款的錢數(shù) private double depositAmount; public DepositThread(String name , Account account , double depositAmount) { super(name); this.account = account; this.depositAmount = depositAmount; } // 重復(fù)100次執(zhí)行存款操作 public void run() { for (int i = 0 ; i < 100 ; i++ ) { account.deposit(depositAmount); } } }
4 測(cè)試類
public class DrawTest { public static void main(String[] args) { // 創(chuàng)建一個(gè)賬戶 Account acct = new Account("1234567" , 0); new DrawThread("取錢者" , acct , 800).start(); new DepositThread("存款者甲" , acct , 800).start(); new DepositThread("存款者乙" , acct , 800).start(); new DepositThread("存款者丙" , acct , 800).start(); } }
三 運(yùn)行結(jié)果
......
存款者丙 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者甲 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者丙 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者甲 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者丙 存款:800.0
賬戶余額為:800.0
取錢者 取錢:800.0
賬戶余額為:0.0
存款者甲 存款:800.0
賬戶余額為:800.0
更多java相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Java進(jìn)程與線程操作技巧總結(jié)》、《Java數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Java操作DOM節(jié)點(diǎn)技巧總結(jié)》、《Java文件與目錄操作技巧匯總》和《Java緩存操作技巧匯總》
希望本文所述對(duì)大家java程序設(shè)計(jì)有所幫助。
相關(guān)文章
MyBatis加載映射文件和動(dòng)態(tài)代理的實(shí)現(xiàn)
本文主要介紹了MyBatis加載映射文件和動(dòng)態(tài)代理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05Java畢業(yè)設(shè)計(jì)實(shí)戰(zhàn)之線上水果超市商城的實(shí)現(xiàn)
這是一個(gè)使用了java+SSM+springboot+redis開發(fā)的網(wǎng)上水果超市商城,是一個(gè)畢業(yè)設(shè)計(jì)的實(shí)戰(zhàn)練習(xí),具有水果超市商城該有的所有功能,感興趣的朋友快來看看吧2022-01-01Java中的System類、BigInteger類和BigDecimal類詳解
這篇文章主要介紹了Java中的System類、BigInteger類和BigDecimal類詳解,arraycopy()方法,復(fù)制數(shù)組元素,比較適合底層調(diào)用,一般使用Arrays.copyOf()完成復(fù)制數(shù)組,需要的朋友可以參考下2023-09-09Java?MyBatis傳出參數(shù)resultType和resultMap解讀
這篇文章主要介紹了Java?MyBatis傳出參數(shù)resultType和resultMap解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12解決StringBuffer和StringBuilder的擴(kuò)容問題
這篇文章主要介紹了解決StringBuffer和StringBuilder的擴(kuò)容問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07JDBC連接MySQL數(shù)據(jù)庫批量插入數(shù)據(jù)過程詳解
這篇文章主要介紹了JDBC連接MySQL數(shù)據(jù)庫批量插入數(shù)據(jù)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11Java class文件格式之方法_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)介紹了Java class文件格式之方法的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-06-06