生產者消費者模式是研究多線程程式的經典問題之一,它描述是有一塊緩衝區作為緩衝隊列/倉庫,生產者可以將產品放入隊列,消費者則可以從隊列中取走產品。大多數的後台服務程式並發控制的基本原理都可以歸納為生產者消費者模式。
1、使用Synchronized()、wait() 、 notify()、notifyAll()方法實現:
package proAndCsmModel01;import java.util.LinkedList;/** * 實現緩衝區 * */public class Resource01 { //最大緩衝區 private final int MAX_SIZE = 10; //緩衝區隊列 LinkedList<datatype> list = new LinkedList<>(); /** * 生產資料同步方法 */ public synchronized void increaseData(){ while (list.size() >= MAX_SIZE){ try { System.out.println(Thread.currentThread().getId()+"資料倉庫已滿!"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } datatype d = new datatype(); d.setData((int) (Math.random()*1000)); list.add(d); System.out.println(Thread.currentThread().getId()+"生產:"+d.getData()+" 庫存量:"+list.size()); notifyAll(); } /** * 消費資料同步方法 */ public synchronized void decreaseData(){ while (list.size() <= 0){ try { System.out.println(Thread.currentThread().getId()+"資料倉庫為空白!"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } datatype d = list.poll(); System.out.println(Thread.currentThread().getId()+"消費:"+d.getData()+" 庫存量:"+list.size()); notifyAll(); }}package proAndCsmModel01;/** * 生產者:生產資料 * */public class producer01 implements Runnable { private Resource01 resource01; producer01(Resource01 resource01){ this.resource01 = resource01; } @Override public void run() { while (true){ try { //隨機休眠後在生產資料 Thread.sleep((long) (Math.random()*1000)); } catch (InterruptedException e) { e.printStackTrace(); } //調用同步方法進行生產資料 resource01.increaseData(); } }}package proAndCsmModel01;/** * 消費者:消費資料 * */public class consumer01 implements Runnable { private Resource01 resource01; consumer01(Resource01 resource01){ this.resource01 = resource01; } @Override public void run() { while (true){ try { //隨機休眠後再消費 Thread.sleep((long) (Math.random()*1000)); } catch (InterruptedException e) { e.printStackTrace(); } //調用同步方法進行消費資料 resource01.decreaseData(); } }}package proAndCsmModel01;/** * 基礎資料型別 (Elementary Data Type) * */public class datatype { private int data; public void setData(int data) { this.data = data; } public int getData() { return data; }}package proAndCsmModel01;/** * 調用 * */public class test { public static void main(String agrs[]){ Resource01 resource01 = new Resource01();; System.out.println(Thread.currentThread().getName()); new Thread(new producer01(resource01)).start(); new Thread(new producer01(resource01)).start(); new Thread(new producer01(resource01)).start(); new Thread(new producer01(resource01)).start(); new Thread(new producer01(resource01)).start(); new Thread(new producer01(resource01)).start(); new Thread(new consumer01(resource01)).start(); new Thread(new consumer01(resource01)).start(); new Thread(new consumer01(resource01)).start(); }}
2、ReentrantLock、await() 、signal()、signalAll()方法實現:
package exampletest.proAndCsmModel01;import java.util.LinkedList;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * 實現緩衝區 * */public class Resource02 { //最大緩衝區 private final int MAX_SIZE = 10; //緩衝區隊列 LinkedList<datatype> list = new LinkedList<>(); //同步鎖和條件 private Lock lock = new ReentrantLock(); private Condition full_cd = lock.newCondition(); private Condition empty_cd = lock.newCondition(); /** * 生產資料同步方法 */ public void increaseData(){ lock.lock(); while (list.size() >= MAX_SIZE){ try { System.out.println(Thread.currentThread().getId()+"資料倉庫已滿!"); full_cd.await(); } catch (InterruptedException e) { e.printStackTrace(); } } datatype d = new datatype(); d.setData((int) (Math.random()*1000)); list.add(d); System.out.println(Thread.currentThread().getId()+"生產:"+d.getData()+" 庫存量:"+list.size()); empty_cd.signalAll(); lock.unlock(); } /** * 消費資料同步方法 */ public void decreaseData(){ lock.lock(); while (list.size() <= 0){ try { System.out.println(Thread.currentThread().getId()+"資料倉庫為空白!"); empty_cd.await(); } catch (InterruptedException e) { e.printStackTrace(); } } datatype d = list.poll(); System.out.println(Thread.currentThread().getId()+"消費:"+d.getData()+" 庫存量:"+list.size()); full_cd.signalAll(); lock.unlock(); }}
3、使用BlockingQueue阻塞隊列方法實現
package exampletest.proAndCsmModel01;import java.util.LinkedList;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * 實現緩衝區 * */public class Resource03 extends Resource{ //最大緩衝區 private final int MAX_SIZE = 10; //緩衝區隊列 LinkedBlockingDeque<datatype> list = new LinkedBlockingDeque<>(MAX_SIZE); /** * 生產資料同步方法 */ @Override public void increaseData(){ if (list.size() >= MAX_SIZE){ System.out.println(Thread.currentThread().getId()+"資料倉庫已滿!"); } datatype d = new datatype(); d.setData((int) (Math.random()*1000)); try { list.put(d); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getId()+"生產:"+d.getData()+" 庫存量:"+list.size()); } /** * 消費資料同步方法 */ @Override public void decreaseData(){ if (list.size() <= 0){ System.out.println(Thread.currentThread().getId()+"資料倉庫為空白!"); } datatype d = null; try { d = list.take(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getId()+"消費:"+d.getData()+" 庫存量:"+list.size()); }}