生產者消費者問題是一個典型的線程同步問題。
主要有如下實現方式:
wait() notifyAll()
class Queue { //共用隊列的目的用於儲存生產者生產和消費者消費的共用資料 int value = 0; boolean isEmpty = true; public synchronized void put(int v) { if (!isEmpty) { try { System.out.println("生產者等待"); wait(); } catch(Exception e) { e.printStackTrace(); } } value+=v; isEmpty = false; System.out.println("生產者共生產數量:"+v); notify(); } public synchronized int get() { if(isEmpty) { try { System.out.println("消費者等待"); wait(); } catch (Exception e) { e.printStackTrace(); } } value--; if (value < 1) { isEmpty = true; } System.out.println("消費者消費一個,剩餘:"+value); notify(); return value; }}
//Producer.java檔案class Producer extends Thread { Queue q; Producer(Queue q) { this.q = q; } public void run() { for (int i = 1;i < 5; i++) { q.put(i); } }}class Consumer extends Thread { Queue q; Consumer(Queue q) { this.q = q; } public void run() { while(true) { q.get(); } }}
//Test.java檔案public class <span style="line-height: 25.2000007629395px; font-family: Tahoma, Helvetica, Arial, 宋體, sans-serif;">Test</span><span style="line-height: 25.2000007629395px; font-family: Tahoma, Helvetica, Arial, 宋體, sans-serif;">{</span> public static void main(String[] args) { Queue q = new Queue(); Producer p = new Producer(q); Consumer c = new Consumer(q); c.start(); p.start(); }}
java.util.concurrent.BlockingQueue 實現
BlockingQueue中
其中有兩個重要的阻塞方法:
put()和take()
put():生產商品,當生產商品已經填充滿倉庫,進入阻塞狀
態,喚醒消費者調 用 take()方法 。
take():商品消費完,進入阻塞狀態,喚醒生產者。
這兩個方法的實現用到了Lock和Condition
package kevin;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;/** * 一個簡單的生產者-消費者demo * * @author KevinJom */public class BlockingQueueDemo {public static void main(String[] args) {new BlockingQueueDemo().go();}private void go() {// 這裡簡單的說一下BlockingQueue的實現,它基於生產者-消費者模式,其中有兩個重要的阻塞方法// put()和take(),而這兩個方法的實現用到了Lock和Condition,具體實現請參考APIBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);Thread t1 = new Thread(new Producer(queue, 500, "peak")); // 生產者線程,來,生產一些i// can// play吧,並且要比nike生產的快Thread t2 = new Thread(new Producer(queue, 1000, "nike")); // 第二個生產者線程Thread t3 = new Thread(new Customer(queue)); // 消費者線程t1.start();t2.start();t3.start();}private class Producer implements Runnable {private BlockingQueue<String> queue;private int timeout; // 生產一個產品後暫停時間private String category; // 僅僅起標記產品作用public Producer(BlockingQueue<String> queue, int timeout,String category) {super();this.queue = queue;this.timeout = timeout;this.category = category;}@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {try {// put()方法也是一個會阻塞的方法,如果隊列已滿的時候這個方法會一起阻塞直到// 隊列中重新出現空間為止queue.put("product " + category);} catch (InterruptedException e1) {e1.printStackTrace();}try {TimeUnit.MILLISECONDS.sleep(timeout); // 每生產一個產品就暫停timeout毫秒} catch (InterruptedException e) {e.printStackTrace();}}}}private class Customer implements Runnable {private BlockingQueue<String> queue;public Customer(BlockingQueue<String> queue) {super();this.queue = queue;}@Overridepublic void run() {while (!Thread.currentThread().isInterrupted()) {try {System.out.println("product got:" + queue.take());} catch (InterruptedException e1) {e1.printStackTrace();}try {// 暫停10毫秒,這裡主要是為了證明take()是一個阻塞方法,如果 BlockingQueue中// 沒有元素,它會一起阻塞直到隊列中有元素為止TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}}