1. 程式人生 > >多生產者多消費者問題(Lock介面、Condition介面)

多生產者多消費者問題(Lock介面、Condition介面)

在多生產者多消費者問題中,我們通過while判斷和notifyAll()全喚醒方法解決了問題,但是notifyAll()同時也帶來了弊端,它要喚醒所有的被等待的執行緒,意味著既喚醒了對方,也喚醒了本方,在喚醒本方執行緒後還要不斷判斷標記,就降低了程式的效率。我們希望只喚醒對方的執行緒。

在jdk進行升級後,就解決了類似的問題。

Lock(介面)實現提供了比使用synchronized方法和語句可獲得的更廣泛的鎖定操作。

synchronized同步程式碼塊格式:

Object obj=new Object();
void show()
{
	synchronized(obj)
	{	
		//需要被同步的程式碼
	}
}
注:同步程式碼塊,對於鎖的操作是隱式的。

jdk1.5以後將同步和鎖封裝成了物件,並將操作鎖的隱式方式定義到了該物件中,將隱式動作變成了顯示動作。

下面採用jdk新特性實現Lock介面自定義鎖實現同步:

Lock lock=new ReentrantLock();  //ReentrantLock類是Lock類的子類,實現Lock介面
void show()
{
	try
	{
		lock.lock();	//獲取鎖
		//程式碼
	}
	
	finally
	{
		lock.unlock();	//釋放鎖
	}
	
}
我們將多生產者多消費者的例子(多生產者多消費者問題)進行改寫:
import  java.util.concurrent.locks.*; //Lock是其他包中的類,使用時需要匯入
//資源
class Resource
{
	private String name;
	private int count=1;
	private boolean flag=false; //設定標誌位值為false,表示沒有被生產

	Lock lock=new ReentrantLock();  //實現一個介面

	public void set(String name)
	{
		lock.lock();
		try
		{
			while(flag)  //while保證在此處被喚醒的執行緒重新判斷標誌位
		       try{this.wait();} catch(InterruptedException e){}	
			this.name=name+count;
			count++;
			System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);
			flag=true;
			notifyAll();  //保證能夠喚醒消費者執行緒
		}
		finally
		{
			lock.unlock();		
		}
	}
	
	public synchronized void out()
	{
		lock.lock();
		try
		{
			while(!flag)
			try{this.wait();} catch(InterruptedException e){}
			System.out.println(Thread.currentThread().getName()+"...消費者......"+this.name);
			flag=false;
			notifyAll();  //保證能夠喚醒生產者執行緒
		}
		finally
		{
			lock.unlock();
		}		
	}
}

//生產者
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鴨");
		}
	}
}


//消費者
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}

class ProducerConsumerDemo
{
	public static void main(String[] args)
	{
		Resource r=new Resource();
		Producer pro=new Producer(r);
		Consumer con=new Consumer(r);

		//2個生產者,2個消費者
		Thread t0=new Thread(pro);
		Thread t1=new Thread(pro);
		Thread t2=new Thread(con);
		Thread t3=new Thread(con);

		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}

執行結果:

但是在上例中,我們用到的鎖是lock,不再是this,而程式碼中呼叫wait()、notifyAll()方法的鎖仍是this,就會出現錯誤,在jdk新特性中,用Lock來代替synchronized,用condition來代替監視器方法(wait()、notify()、notifyAll())。

condition介面:

interface Condition  //Condition介面,替代Object類裡的wait()、notify()、notifyAll()方法
{
	await();
	signal();
	signalAll();
}
 
Lock lock=new ReectrantLock();    //一個鎖上建立兩個監視器,一個鎖上就有多個監視器方法
Condition c1=lock.newCondition(); //每個監視器裡都有await()、signal()、signalAll()方法
Condition c2=lock.newCondition();
還是要將上例繼續進行改寫:
import  java.util.concurrent.locks.*; //Lock是其他包中的類,使用時需要匯入
//資源
class Resource
{
	private String name;
	private int count=1;
	private boolean flag=false; //設定標誌位值為false,表示沒有被生產

	//建立一個鎖物件
	Lock lock=new ReentrantLock();  //實現一個介面

	//通過已有的鎖獲取該鎖上的監視器物件
	Condition con=lock.newCondition();  //con是lock鎖上的一個監視器

	public void set(String name)
	{
		lock.lock();
		try
		{
			while(flag)  //while保證在此處被喚醒的執行緒重新判斷標誌位
			//try{lock.wait();} catch(InterruptedException e){}
		        try{con.await();} catch(InterruptedException e){}	
			this.name=name+count;
			count++;
			System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);
			flag=true;
			//notifyAll();  //保證能夠喚醒消費者執行緒
			con.signalAll();
		}
		finally
		{
			lock.unlock();		
		}
	}
	
	public synchronized void out()
	{
		lock.lock();
		try
		{
			while(!flag)
			//try{this.wait();} catch(InterruptedException e){}
			try{con.await();} catch(InterruptedException e){}
			System.out.println(Thread.currentThread().getName()+"...消費者......"+this.name);
			flag=false;
			//notifyAll();  //保證能夠喚醒生產者執行緒
			con.signalAll();
		}
		finally
		{
			lock.unlock();
		}		
	}
}

//生產者
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鴨");
		}
	}
}


//消費者
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}

class ProducerConsumerDemo
{
	public static void main(String[] args)
	{
		Resource r=new Resource();
		Producer pro=new Producer(r);
		Consumer con=new Consumer(r);

		//2個生產者,2個消費者
		Thread t0=new Thread(pro);
		Thread t1=new Thread(pro);
		Thread t2=new Thread(con);
		Thread t3=new Thread(con);

		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}
執行結果:



在前面我們提到希望只喚醒對方的執行緒,來提升程式執行的效率。如果程式中只有一個監視器,那個它能將生產者消費者全部wait,和生產者消費者全部喚醒。實際我們希望生產者喚醒消費者,消費者喚醒生產者,這樣我們建立兩組監視器,一組監視生產者,一組監視消費者。

import  java.util.concurrent.locks.*; //Lock是其他包中的類,使用時需要匯入
//資源
class Resource
{
	private String name;
	private int count=1;
	private boolean flag=false; //設定標誌位值為false,表示沒有被生產

	//建立一個鎖物件
	Lock lock=new ReentrantLock();  //實現一個介面

	//通過已有的鎖獲取該鎖上的監視器物件
	//Condition con=lock.newCondition();  //con是lock鎖上的一個監視器

	//通過已有的鎖獲取兩組監視器,一組監視生產者,一組監視消費者
	Condition producer_con=lock.newCondition();    //生產者的監視器
	Condition consumer_con=lock.newCondition();    //消費者的監視器

	public void set(String name)
	{
		lock.lock();
		try
		{
			while(flag)  //while保證在此處被喚醒的執行緒重新判斷標誌位
			//try{lock.wait();} catch(InterruptedException e){}
		        try{producer_con.await();} catch(InterruptedException e){} //生產者執行緒等待	
			this.name=name+count;
			count++;
			System.out.println(Thread.currentThread().getName()+"...生產者..."+this.name);
			flag=true;
			//notifyAll();  //保證能夠喚醒消費者執行緒
			//con.signalAll();
			consumer_con.signal(); //喚醒消費者的一個執行緒
		}
		finally
		{
			lock.unlock();		
		}
	}
	
	public synchronized void out()
	{
		lock.lock();
		try
		{
			while(!flag)
			//try{this.wait();} catch(InterruptedException e){}
			try{consumer_con.await();} catch(InterruptedException e){}  //消費者執行緒等待
			System.out.println(Thread.currentThread().getName()+"...消費者......"+this.name);
			flag=false;
			//notifyAll();  //保證能夠喚醒生產者執行緒
			//con.signalAll();
			producer_con.signal(); //喚醒生產者的一個執行緒
		}
		finally
		{
			lock.unlock();
		}		
	}
}

//生產者
class Producer implements Runnable
{
	private Resource r;
	Producer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.set("烤鴨");
		}
	}
}


//消費者
class Consumer implements Runnable
{
	private Resource r;
	Consumer(Resource r)
	{
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
}

class ProducerConsumerDemo
{
	public static void main(String[] args)
	{
		Resource r=new Resource();
		Producer pro=new Producer(r);
		Consumer con=new Consumer(r);

		//2個生產者,2個消費者
		Thread t0=new Thread(pro);
		Thread t1=new Thread(pro);
		Thread t2=new Thread(con);
		Thread t3=new Thread(con);

		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}
執行結果:

總結:

一:Lock介面:出現替代了同步程式碼塊或者同步函式,將同步的隱式鎖操作變成現實鎖操作,同時更為靈活,可以一個鎖上加上多組監視器。

方法:lock():獲取鎖

            unlock():釋放鎖,通常需要定義在finally程式碼塊中

二:Condition介面:出現替代了Object中 的wait  notify  notifyAll方法,將這些監視器方法單獨進行了封裝,變成Condition監視器物件,可以任意鎖進行組合。

方法:await()

            signal()

            signalAll()