一,java多線程使用Object對象的wait,notify或者notifyAll方法進行通信
java的Object對象提供wait,notify和notifyAll方法。根據api中Object對象描述-------這三個方法調用時,應該只由對象監視器的所有者調用。即只能由synchronized所獲得的鎖對象進行調用,且只能在synchronized同步方法或者同步代碼塊中調用。
- 使用synchronized同步代碼塊時,同步鎖對象爲括號內的同步鎖對象,必須使用該對象來調用這三個方法
- 使用synchronized修飾的同步方法時,同步鎖對象爲該類的默認實例,即this所指代的對象。對於靜態類,鎖對象爲該類的字節碼文件
<span style="font-size:14px;">package thread;
/*
* 假設系統中有多條線程,這些線程分別代表取錢者和存錢者。現在系統有一種特殊的要求,
* 系統要求存款者和取錢者不斷的實現存款和取錢動作,而且要求每當存款者將錢存入指定賬戶後,
* 取錢者立即將錢取走.不允許存款者兩次存錢,也不允許取錢者兩次取錢.
* 我們通過設置一個旗標來標識賬戶中是否已有存款,有就爲true,沒有就標爲false。
*/
public class Draw {
public static void main(String[] args) {
Acount acount = new Acount();
new Thread(new Custom(acount, 800), "fupeng").start();
new Thread(new Producer(acount, 800), "xiaoming1").start();
new Thread(new Producer(acount, 800), "xiaoming2").start();
new Thread(new Producer(acount, 800), "xiaoming3").start();
new Thread(new Producer(acount, 800), "xiaoming4").start();
new Thread(new Producer(acount, 800), "xiaoming5").start();
}
}
class Custom implements Runnable{
private Acount acount;
private int drawMoney;
public Custom(Acount acount,int drawMoney){
this.acount = acount;
this.drawMoney = drawMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.drawMoney(drawMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
private Acount acount;
private int saveMoney;
public Producer(Acount acount, int saveMoney) {
super();
this.acount = acount;
this.saveMoney = saveMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.saveMoney(saveMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Acount{
private int money;
private boolean flag = false;
public synchronized void saveMoney(int saveMoney) {
if(flag){
try {
System.out.println(Thread.currentThread().getName()+"wait並釋放acount對象鎖");
this.wait();
System.out.println("繼續運行");
}catch (InterruptedException e){
e.printStackTrace();}
}
this.money+=saveMoney;
System.out.println(Thread.currentThread().getName()+"存入:"+saveMoney);
flag = true;
this.notifyAll();
}
public synchronized void drawMoney(int drawMoney){
if(!flag){
try {
System.out.println(Thread.currentThread().getName()+" wait並釋放acount對象鎖");
wait();
System.out.println("繼續運行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.money-=drawMoney;
System.out.println(Thread.currentThread().getName()+"取出:"+drawMoney);
System.out.println("餘額:"+money);
flag = false ;
notifyAll();
}
}</span>
在上述代碼示例中,存在一個消費者線程,5個生產者線程。按照要求應該是:當有一個生產者線程存入錢之後,其他生產者線程不能再存入,只能等消費者取出之後再行存入。但是按照上述代碼,運行中出現多個線程同時連續存入:
<strong>fupeng wait並釋放acount對象鎖
xiaoming5存入:800
繼續運行
fupeng取出:800
餘額:0
xiaoming1存入:800
xiaoming2wait並釋放acount對象鎖
xiaoming4wait並釋放acount對象鎖
xiaoming3wait並釋放acount對象鎖
xiaoming1wait並釋放acount對象鎖
xiaoming5wait並釋放acount對象鎖
fupeng取出:800
餘額:0
繼續運行
xiaoming5存入:800
繼續運行
xiaoming1存入:800
繼續運行
xiaoming3存入:800
繼續運行
xiaoming4存入:800
繼續運行
xiaoming2存入:800
fupeng取出:800
餘額:3200</strong>
當消費者取出之後,5個生產者線程連續存入。
分析結果:當xiaoming1線程存入800之後,xiaoming2........xiaoming5執行存入操作,但flag標誌爲true,執行wait方法,釋放同步鎖,進入等待線程集。當fupeng線程取出之後,喚醒所有生產者線程,生產者線程接着獲取最新狀態同步鎖對象,並接着從上次執行wait方法的地方接着繼續執行,故出現1,2,3,4,5連續存入的現象。
注:在java多線程通信的方法中,當使用Object的wait,notify,notifyAll方法時:
若線程執行wait方法,該線程釋放同步鎖對象,並進入等待狀態。當該線程被其他線程喚醒之後,重新獲取最新狀態的同步鎖對象,並接着上次執行wait方法的地方繼續執行。
修改方法:
上述代碼執行之後出現連續存入現象,全因爲線程執行wait方法進入等待線程集之後,再被喚醒之後是繼續接着上次wait方法的地方執行,故將wait後的代碼放入else代碼塊中即可。
public class Draw {
public static void main(String[] args) {
Acount acount = new Acount();
new Thread(new Custom(acount, 800), "fupeng").start();
new Thread(new Producer(acount, 800), "xiaoming1").start();
new Thread(new Producer(acount, 800), "xiaoming2").start();
new Thread(new Producer(acount, 800), "xiaoming3").start();
new Thread(new Producer(acount, 800), "xiaoming4").start();
new Thread(new Producer(acount, 800), "xiaoming5").start();
}
}
class Custom implements Runnable{
private Acount acount;
private int drawMoney;
public Custom(Acount acount,int drawMoney){
this.acount = acount;
this.drawMoney = drawMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.drawMoney(drawMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
private Acount acount;
private int saveMoney;
public Producer(Acount acount, int saveMoney) {
super();
this.acount = acount;
this.saveMoney = saveMoney;
}
@Override
public void run() {
while(true){
try {
Thread.sleep(1000);
this.acount.saveMoney(saveMoney);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Acount{
private int money;
private boolean flag = false;
public synchronized void saveMoney(int saveMoney) {
if(flag){
try {
System.out.println(Thread.currentThread().getName()+"wait並釋放acount對象鎖");
this.wait();
System.out.println(Thread.currentThread().getName()+"繼續運行");
}catch (InterruptedException e){
e.printStackTrace();}
}else{
this.money+=saveMoney;
System.out.println(Thread.currentThread().getName()+"存入:"+saveMoney);
flag = true;
this.notifyAll();
}
}
public synchronized void drawMoney(int drawMoney){
if(!flag){
try {
System.out.println(Thread.currentThread().getName()+" wait並釋放acount對象鎖");
wait();
System.out.println(Thread.currentThread().getName()+"繼續運行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
this.money-=drawMoney;
System.out.println(Thread.currentThread().getName()+"取出:"+drawMoney);
System.out.println("餘額:"+money);
flag = false ;
notifyAll();
}
}
}
執行結果:
fupeng wait並釋放acount對象鎖
xiaoming2存入:800
fupeng繼續運行
xiaoming1wait並釋放acount對象鎖
xiaoming3wait並釋放acount對象鎖
xiaoming4wait並釋放acount對象鎖
xiaoming5wait並釋放acount對象鎖
xiaoming2wait並釋放acount對象鎖
fupeng取出:800
餘額:0
xiaoming2繼續運行
xiaoming5繼續運行
xiaoming4繼續運行
xiaoming3繼續運行
xiaoming1繼續運行
xiaoming2存入:800
xiaoming1wait並釋放acount對象鎖
xiaoming3wait並釋放acount對象鎖
xiaoming4wait並釋放acount對象鎖
xiaoming5wait並釋放acount對象鎖
fupeng取出:800
餘額:0
xiaoming2線程存入之後,生產者線程進入等待狀態, 消費者取出錢之後notifyAll生產者線程,從上次wait方法的地方接着執行,執行完上次被打斷的saveMoney方法。
接着xiaoming2線程又獲取cpu執行資源,存入錢。。。。。。