java編寫生產者/消費者模式的程式。

麒麟NG發表於2020-11-18

題目要求

編寫生產者/消費者模式的程式。生產者每隔100ms產生一個0~9之間的一個數,儲存在一個MyNumber型別的物件中,並顯示出來。只要這個MyNumber物件中儲存了新的數字,消費者就將其取出來顯示。試定義MyNumber類,編寫消費者、生產者程式,並編寫主程式建立一個MyNumber物件,一個生產者執行緒、一個消費者執行緒並將這兩個執行緒啟動。

簡單思路描述:
簡單思路:
生產者在100ms內生產0~9的一個數字,先生產後消費。在消費者進行消費時候生產者需要等待。
消費者從物件中讀出數字:
MyNumber相當於一個池,執行消費行為前需要對池進行判斷
如果池為空,則消費者不能夠對其執行操作,反之可以。

使用訊號燈法:flagflag為true時,生產者開始生產,消費者等待
//flag為false時,消費者開始消費,生產者等待

下面的文字均來自其它部落格和網頁。

public synchronized(同步) void MyNumber(){}所解決的問題:
由於同一程式的多個執行緒共享同一片儲存空間,在帶來方便的同時,也帶來了訪問衝突這個嚴重的問題。Java語言提供了專門機制以解決這種衝突,有效避免了同一個資料物件被多個執行緒同時訪問。
synchronized關鍵字可以作為函式的修飾符,也可作為函式內的語句,也就是平時說的同步方法和同步語句塊。如果 再細的分類,synchronized可作用於instance變數、object reference(物件引用)、static函式和class literals(類名稱字面常量)身上。
無論synchronized關鍵字加在方法上還是物件上,它取得的鎖都是物件,而不是把一段程式碼或函式當作鎖――而且同步方法很可能還會被其他執行緒的物件訪問。
每個物件只有一個鎖(lock)與之相關聯。
實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以儘量避免無謂的同步控制。
synchronized關鍵字的作用域有二種:
某個物件例項內,synchronized aMethod(){}可以防止多個執行緒同時訪問這個物件的synchronized方法(如果一個物件有多個synchronized方法,只要一個線 程訪問了其中的一個synchronized方法,其它執行緒不能同時訪問這個物件中任何一個synchronized方法)。這時,不同的物件例項的 synchronized方法是不相干擾的。也就是說,其它執行緒照樣可以同時訪問相同類的另一個物件例項中的synchronized方法;
某個類的範圍,synchronized static aStaticMethod{}防止多個執行緒同時訪問這個類中的synchronized static 方法。它可以對類的所有物件例項起作用。

注意:
synchronized 方法
每個類例項對應一把鎖,每個 synchronized 方法都必須獲得呼叫該方法的類例項的鎖方能執行,否則所屬執行緒阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的執行緒方能獲得該鎖,重新進入可執行狀態。這種機制確保了同一時刻對於每一個類例項,其所有宣告為 synchronized 的成員函式中至多隻有一個處於可執行狀態(因為至多隻有一個能夠獲得該類例項對應的鎖),從而有效避免了類成員變數的訪問衝突(只要所有可能訪問類成員變數的方法均被宣告為 synchronized)。
在 Java 中,不光是類例項,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函式宣告為 synchronized ,以控制其對類的靜態成員變數的訪問。

簡單來說synchronized解決了在多執行緒的環境下,控制synchronized程式碼段不被多個執行緒同時執行。

package Pro_Con;

public class Center{
	
	public static void main(String[] args) {
		//共有資源
		MyNumber m= new MyNumber();
		//多執行緒
		production a=new production(m);
		consumption b=new consumption(m);
		
		Thread t1=new Thread(a);
		Thread t2=new Thread(b);
		t1.start();
		t2.start();
		
		t1.stop();
		t2.stop();
	}
}

package Pro_Con;

public class consumption implements Runnable {
	private MyNumber m;
	
	public consumption(MyNumber m) {
		super();
		this.m=m;
	}
	
	@Override
	public void run() {
		for(int i=0;i<20;i++) {
				m.consumption();
		}
	}

}

package Pro_Con;

public class production implements Runnable{
	private MyNumber m;
	public production(MyNumber m) {
		super();
		this.m=m;
	}
	
	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			if(0==i%2) {
				m.production(i);
			}else {
				m.production(i);
			}
		}
	}
}

package Pro_Con;

public class MyNumber {
	private int pic;
	private boolean Flag=true;
	//flag為true時,生產者開始生產,消費者等待
	//flag為false時,消費者開始消費,生產者等待
	
	public synchronized void production(int pic) {
		if(!Flag) try{
			this.wait();
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		
	//開始生產
	try {
		Thread.sleep(100);
	}catch(InterruptedException e) {
		e.printStackTrace();
	}
	System.out.println("已經生產了"+pic);
	this.pic=pic;
	this.notify();//通知消費者可以消費
	Flag=false;
	}
	
	public synchronized void consumption() {
		if(Flag) try{
		this.wait();	
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
		
	//開始消費
	try {
		Thread.sleep(100);
	}catch(InterruptedException e) {
		e.printStackTrace();
	}
	System.out.println("消費了"+pic);
	this.notify();
	this.Flag=true;
	}
}

未完待續

相關文章