? ? ? ? 條件變量是Java5線程中很重要的一個概念,顧名思義,條件變量就是表示條件的一種變量。但是必須說明,這里的條件是沒有實際含義的,僅僅是個標記而已,并且條件的含義往往通過代碼來賦予其含義。
????????這里的條件和普通意義上的條件表達式有著天壤之別。
????????條件變量都實現了java.util.concurrent.locks.Condition接口,條件變量的實例化是通過一個Lock對象上調用newCondition()方法來獲取的,這樣,條件就和一個鎖對象綁定起來了。因此,Java中的條件變量只能和鎖配合使用,來控制并發程序訪問競爭資源的安全。
????????條件變量的出現是為了更精細控制線程等待與喚醒,在Java5之前,線程的等待與喚醒依靠的是Object對象的wait()和notify()/notifyAll()方法,這樣的處理不夠精細。
????????而在Java5中,一個鎖可以有多個條件,每個條件上可以有多個線程等待,通過調用await()方法,可以讓線程在該條件下等待。當調用signalAll()方法,又可以喚醒該條件下的等待的線程。有關Condition接口的API可以具體參考JavaAPI文檔。
????????條件變量比較抽象,原因是他不是自然語言中的條件概念,而是程序控制的一種手段。
????????下面以一個銀行存取款的模擬程序為例來揭蓋Java多線程條件變量的神秘面紗:
????????有一個賬戶,多個用戶(線程)在同時操作這個賬戶,有的存款有的取款,存款隨便存,取款有限制,不能透支,任何試圖透支的操作都將等待里面有足夠存款才執行操作。
import?java.util.concurrent.ExecutorService;??
import?java.util.concurrent.Executors;??
import?java.util.concurrent.locks.Condition;??
import?java.util.concurrent.locks.Lock;??
import?java.util.concurrent.locks.ReentrantLock;??/**?*?Java線程:條件變量?*/??
public?class?Test?{??public?static?void?main(String[]?args){??//創建并發訪問的賬戶??MyCount?myCount=new?MyCount("6215580000000000000",10000);??//創建一個線程池??ExecutorService?pool?=?Executors.newFixedThreadPool(2);??Thread?t1?=?new?SaveThread("張三",?myCount,?2000);??Thread?t2?=?new?SaveThread("李四",?myCount,?3600);??Thread?t3?=?new?DrawThread("王五",?myCount,?2700);??Thread?t4?=?new?SaveThread("老張",?myCount,?600);??Thread?t5?=?new?DrawThread("老牛",?myCount,?1300);??Thread?t6?=?new?DrawThread("胖子",?myCount,?800);??//執行各個線程??pool.execute(t1);??pool.execute(t2);??pool.execute(t3);??pool.execute(t4);??pool.execute(t5);??pool.execute(t6);??//關閉線程池??pool.shutdown();??}??
}??
//存款線程類??
class?SaveThread?extends?Thread?{??private?String?name;????????????//操作人??private?MyCount?myCount;????????//賬戶??private?int?x;??????????????????//存款金額??SaveThread(String?name,?MyCount?myCount,?int?x)?{??this.name?=?name;??this.myCount?=?myCount;??this.x?=?x;??}??public?void?run()?{??myCount.saving(x,?name);??}??
}??
//取款線程類??
class?DrawThread?extends?Thread?{??private?String?name;????????????????//操作人??private?MyCount?myCount;????????//賬戶??private?int?x;????????????????????????????//存款金額??DrawThread(String?name,?MyCount?myCount,?int?x)?{??this.name?=?name;??this.myCount?=?myCount;??this.x?=?x;??}??public?void?run()?{??myCount.drawing(x,?name);??}??
}??
//普通銀行賬戶,不可透支??
class?MyCount?{??private?String?oid;????????????????????????//賬號??private?int?cash;????????????????????????????//賬戶余額??private?Lock?lock?=new?ReentrantLock();?????????//賬戶鎖??private?Condition?_save?=?lock.newCondition();????//存款條件??private?Condition?_draw?=?lock.newCondition();????//取款條件??MyCount(String?oid,?int?cash)?{???????this.oid?=?oid;??this.cash?=?cash;??}??/**?*?存款?*?@param?x?存款金額?*?@param?name?存款人?*/??public?void?saving(int?x,String?name){??lock.lock();????????????????????????//獲取鎖??if?(x?>?0)?{??cash?+=?x;?//?存款??System.out.println(name?+?"存款"?+?x?+?",當前余額為"?+?cash);??}??_draw.signalAll();????????????//喚醒所有等待線程。??lock.unlock();????????????????????//釋放鎖??}??//取款??public?void?drawing(int?x,?String?name)?{??lock.lock();?????????????????????????????????//獲取鎖??try?{??if?(cash?-?x?<?0)?{??_draw.await();?//?阻塞取款操作??}?else?{??cash?-=?x;?//?取款??System.out.println(name?+?"取款"?+?x?+?",當前余額為"?+?cash);??}??_save.signalAll();?//?喚醒所有存款操作??}?catch?(InterruptedException?e)?{??e.printStackTrace();??}?finally?{??lock.unlock();?//?釋放鎖??}??}??
}??
????????執行結果:
張三存款2000,當前余額為12000??
王五取款2700,當前余額為9300??
老張存款600,當前余額為9900??
老牛取款1300,當前余額為8600??
胖子取款800,當前余額為7800??
李四存款3600,當前余額為11400??
????????假如我們不用鎖和條件變量,如何實現此功能呢?下面是實現代碼:
import?java.util.concurrent.ExecutorService;??
import?java.util.concurrent.Executors;??/**?*?Java線程:不用條件變量?*/??
public?class?Test?{??public?static?void?main(String[]?args){??//創建并發訪問的賬戶??MyCount?myCount=new?MyCount("6215580000000000000",10000);??//創建一個線程池??ExecutorService?pool?=?Executors.newFixedThreadPool(2);??Thread?t1?=?new?SaveThread("張三",?myCount,?2000);??Thread?t2?=?new?SaveThread("李四",?myCount,?3600);??Thread?t3?=?new?DrawThread("王五",?myCount,?2700);??Thread?t4?=?new?SaveThread("老張",?myCount,?600);??Thread?t5?=?new?DrawThread("老牛",?myCount,?1300);??Thread?t6?=?new?DrawThread("胖子",?myCount,?800);??//執行各個線程??pool.execute(t1);??pool.execute(t2);??pool.execute(t3);??pool.execute(t4);??pool.execute(t5);??pool.execute(t6);??//關閉線程池??pool.shutdown();??}??
}??
//存款線程類??
class?SaveThread?extends?Thread?{??private?String?name;????????????//操作人??private?MyCount?myCount;????????//賬戶??private?int?x;??????????????????//存款金額??SaveThread(String?name,?MyCount?myCount,?int?x)?{??this.name?=?name;??this.myCount?=?myCount;??this.x?=?x;??}??public?void?run()?{??myCount.saving(x,?name);??}??
}??
//取款線程類??
class?DrawThread?extends?Thread?{??private?String?name;????????????????//操作人??private?MyCount?myCount;????????//賬戶??private?int?x;????????????????????????????//存款金額??DrawThread(String?name,?MyCount?myCount,?int?x)?{??this.name?=?name;??this.myCount?=?myCount;??this.x?=?x;??}??public?void?run()?{??myCount.drawing(x,?name);??}??
}??
//普通銀行賬戶,不可透支??
class?MyCount?{??private?String?oid;????????????????????????//賬號??private?int?cash;????????????????????????????//賬戶余額??MyCount(String?oid,?int?cash)?{???????this.oid?=?oid;??this.cash?=?cash;??}??/**?*?存款?*?@param?x?存款金額?*?@param?name?存款人?*/??public?synchronized?void?saving(int?x,Stringname){?????????if?(x?>?0)?{??cash?+=?x;?//?存款??System.out.println(name?+?"存款"?+?x?+?",當前余額為"?+?cash);??}??notifyAll();?//喚醒所有等待線程。??}??//取款??public?synchronized?void?drawing(int?x,?String?name){???????if?(cash?-?x?<?0)?{??try?{??wait();??}?catch?(InterruptedException?e)?{??e.printStackTrace();??}??}?else?{??cash?-=?x;?//?取款??System.out.println(name?+?"取款"?+?x?+?",當前余額為"?+?cash);??}??notifyAll();?//?喚醒所有存款操作???????}??
}??
????????執行結果:
張三存款2000,當前余額為12000??
王五取款2700,當前余額為9300??
李四存款3600,當前余額為12900??
老牛取款1300,當前余額為11600??
胖子取款800,當前余額為10800??
老張存款600,當前余額為11400?
?
????????結合先前同步代碼知識,舉一反三,將此例改為同步代碼塊來實現,代碼如下:
import?java.util.concurrent.ExecutorService;??
import?java.util.concurrent.Executors;??/**?*?Java線程:改用同步代碼塊?*/??
public?class?Test?{??public?static?void?main(String[]?args){??//創建并發訪問的賬戶??MyCount?myCount=new?MyCount("6215580000000000000",10000);??//創建一個線程池??ExecutorService?pool?=?Executors.newFixedThreadPool(2);??Thread?t1?=?new?SaveThread("張三",?myCount,?2000);??Thread?t2?=?new?SaveThread("李四",?myCount,?3600);??Thread?t3?=?new?DrawThread("王五",?myCount,?2700);??Thread?t4?=?new?SaveThread("老張",?myCount,?600);??Thread?t5?=?new?DrawThread("老牛",?myCount,?1300);??Thread?t6?=?new?DrawThread("胖子",?myCount,?800);??//執行各個線程??pool.execute(t1);??pool.execute(t2);??pool.execute(t3);??pool.execute(t4);??pool.execute(t5);??pool.execute(t6);??//關閉線程池??pool.shutdown();??}??
}??
//存款線程類??
class?SaveThread?extends?Thread?{??private?String?name;????????????//操作人??private?MyCount?myCount;????????//賬戶??private?int?x;??????????????????//存款金額??SaveThread(String?name,?MyCount?myCount,?int?x)?{??this.name?=?name;??this.myCount?=?myCount;??this.x?=?x;??}??public?void?run()?{??myCount.saving(x,?name);??}??
}??
//取款線程類??
class?DrawThread?extends?Thread?{??private?String?name;????????????????//操作人??private?MyCount?myCount;????????//賬戶??private?int?x;???????????????????????????//存款金額??DrawThread(String?name,?MyCount?myCount,?int?x)?{??this.name?=?name;??this.myCount?=?myCount;??this.x?=?x;??}??public?void?run()?{??myCount.drawing(x,?name);??}??
}??
//普通銀行賬戶,不可透支??
class?MyCount?{??private?String?oid;????????????????????????//賬號??private?int?cash;????????????????????????????//賬戶余額??MyCount(String?oid,?int?cash)?{???????this.oid?=?oid;??this.cash?=?cash;??}??/**?*?存款?*?@param?x?存款金額?*?@param?name?存款人?*/??public?void?saving(int?x,String?name){????????if?(x?>?0)?{??synchronized?(this)?{??cash?+=?x;?//?存款??System.out.println(name?+?"存款"?+?x?+?",當前余額為"?+?cash);??notifyAll();?//喚醒所有等待線程。??}??}???????}??//取款??public?synchronized?void?drawing(int?x,?String?name)?{??????synchronized?(this)?{??if?(cash?-?x?<?0)?{??try?{??wait();??}catch?(InterruptedException?e)?{??e.printStackTrace();??}??}?else?{??cash?-=?x;?//?取款??System.out.println(name?+?"取款"?+?x?+?",當前余額為"?+?cash);??}???????????????}??notifyAll();?//?喚醒所有存款操作??}??
}??
????????執行結果:
李四存款3600,當前余額為13600??
王五取款2700,當前余額為10900??
老張存款600,當前余額為11500??
老牛取款1300,當前余額為10200??
胖子取款800,當前余額為9400??
張三存款2000,當前余額為11400??
????????對比以上三種方式,從控制角度上講,第一種最靈活,第二種代碼最簡單,第三種容易犯錯。
?
總結
以上是生活随笔為你收集整理的Java线程详解(16)-条件变量的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。