java多線程線程通信


一,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執行資源,存入錢。。。。。。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章