1. 程式人生 > >[24]多執行緒生產與消費

[24]多執行緒生產與消費

 一、用Synchronized程式碼塊

可以根據註釋閱讀程式碼

run()方法解析

package synchronized_compro;
/**
 * 資源類(因為資源共享,所以要是單例)
 * 屬性:商品名稱,商品編號,標識,單例引用
 * */
public class Resource {
	private static final Resource r=new Resource(); //單例
	private String name; //商品名稱
	private int num=1; //商品編號
	private boolean flag=false; //標識,用於標識是該生產還是消費
	
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public boolean getFlag() {
		return flag;
	}
	
	public void setName(String name) {
		this.name = name+num;
		num++;
	}
	public String getName() {
		return name;
	}
	public static Resource getInstance() {
		return r;
	}
}

package synchronized_compro;

/**
 * 生產類(生產一般是多個,所以用執行緒)
   屬性:資源的物件引用 
   行為:生產
 */
public class Producer implements Runnable {
	private Resource r = Resource.getInstance(); // 單例

	/* 生產 */
	public void set(String name) {
		r.setName("商品");
		System.out.println(Thread.currentThread().getName() + " : 生產 " + r.getName());
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
			while (true) {
				synchronized (r) {
				while (r.getFlag()) {
					try {
						r.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				set("商品");
				r.setFlag(true);
				r.notifyAll();
			}
		}
	}
}
package synchronized_compro;

/**
 * 消費類(消費一般是多個,所以用執行緒)
 * 屬性:資源的物件引用
 * 行為:消費
 * */

public class Consumer implements Runnable{
	private Resource r=Resource.getInstance();//單例
	
	/*消費*/
	public void out() {
		System.out.println(Thread.currentThread().getName()+" : 消費 "+r.getName());
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
			while (true) {
				synchronized (r) {
				while (!r.getFlag()) {
					try {
						r.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				out();
				r.setFlag(false);
				r.notifyAll();
			}
		}
	}
}
package synchronized_compro;

/**
 * 測試類
 * */
public class Demo {
	public static void main(String[] args) {
		Producer p=new Producer(); //例項化生產
		Consumer c=new Consumer(); //例項化消費
		
		Thread t1=new Thread(p);
		Thread t2=new Thread(p);
		
		Thread t3=new Thread(c);
		Thread t4=new Thread(c);
		
		t1.start();
		t2.start();
		
		t3.start();
		t4.start();
	}
}

notifyAll()會造成浪費太多資源,降低效能。notify()會產生死鎖。雖說可以使用兩個synchronized巢狀程式碼塊,但是這樣也會造成死鎖

二、利用Lock替代synchronized,利用Condition來替代監視器方法

這樣就可以解決只有一個鎖難以解決佔用資源的缺點

package compro;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 資源類(因為資源共享,所以要是單例)
 * 屬性:商品名稱,商品編號
 * */
public class Resource {
	private static final Resource r=new Resource(); //資源
	private String name; //商品名稱
	private int num=1; //商品編號
	private boolean flag=false; //標識,用於標識是該生產還是消費
	private static Lock lock=new ReentrantLock(); //鎖例項
	private static Condition con1=lock.newCondition(); //生產監視器
	private static Condition con2=lock.newCondition(); //消費監視器
	
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public boolean getFlag() {
		return flag;
	}
	
	public void setName(String name) {
		this.name = name+num;
		num++;
	}
	
	public String getName() {
		return name;
	}
    /*對外開放能獲取資源物件的方法*/
	public static Resource getInstance() {
		return r;
	}
	/*對外開放能獲取Lock物件的方法*/
	public static Lock getLock() {
		return lock;
	}
	/*對外開放能獲取Con1物件的方法*/
	public static Condition getCon1() {
		return con1;
	}
	/*對外開放能獲取Con2物件的方法*/
	public static Condition getCon2() {
		return con2;
	}
}

package compro;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 生產類(生產一般是多個,所以用執行緒
   屬性:資源的物件引用 
   行為:生產
 */
public class Producer implements Runnable {
	private Resource r = Resource.getInstance(); //資源
	private Lock lock = Resource.getLock(); //鎖
	private Condition con1 = Resource.getCon1(); //監視器

	/* 生產 */
	public void set(String name) {
		r.setName("商品");
		System.out.println(Thread.currentThread().getName() + " : 生產 " + r.getName());
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		lock.lock();
		try {
			while (true) {
				while (r.getFlag()) {
					try {
						con1.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				set("商品");
				r.setFlag(true);
				con1.signal();
			}
		} finally {
			// TODO: handle finally clause
			lock.unlock();
		}
	}
}

package compro;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 消費類(消費一般是多個,所以用執行緒)
 * 屬性:資源的物件引用
 * 行為:消費
 * */

public class Consumer implements Runnable{
	private Resource r=Resource.getInstance(); //資源
	private Lock lock=Resource.getLock(); //鎖
	private Condition con2=Resource.getCon2(); //監視器
	
	/*消費*/
	public void out() {
		System.out.println(Thread.currentThread().getName()+" : 消費 "+r.getName());
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		lock.lock();
		try {
			while(true) {
				while(!r.getFlag()) {
					try {
						con2.await();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				out();
				r.setFlag(false);
				con2.signal();
			}
		} finally {
			// TODO: handle finally clause
			lock.unlock();
		}
	}
}

package compro;

/**
 * 測試類
 * */
public class Demo {
	public static void main(String[] args) {
		Producer p=new Producer(); //例項化生產
		Consumer c=new Consumer(); //例項化消費
		
		Thread t1=new Thread(p);
		Thread t2=new Thread(p);
		
		Thread t3=new Thread(c);
		Thread t4=new Thread(c);
		
		t1.start();
		t2.start();
		
		t3.start();
		t4.start();
	}
}

Synchronized程式碼塊的鎖,是物件的鎖。每個物件都會有一個隱式的鎖。會自動的開啟和關閉。

而Lock,需要顯式的進行開啟和關閉是,並且關閉是必須的,不然不關閉的話,就會佔用資源。所以利用try/finally的組合,無論怎麼樣都要關閉。

Condition的作用是則是監視器,利用await()和signal()代替了Object中的wait()和notify()。