生产者和消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一个存储空间,如下图所示,生产者向空间里存放数据,而消费者取用数据,
如果不加以协调可能会出现以下情况:
存储空间已满,而生产者占用着它,消费者等着生产者让出空间从而去除产品,生产者等着消费者消费产品,从而向空间中添加产品。互相等待,从而发生死锁。
存储空间为空,而消费者占用着它,生产者等着消费者让出空间从而添加产品,消费者等着生产者生产产品,从而从空间中去除产品。互相等待,从而发生死锁。
JAVA解决线程模型的三种方式
1、wait()和notify()
import java.util.LinkedList; public class ProducerConsumer { private LinkedList<Object> storeHouse = new LinkedList<Object>(); private int MAX = 10; public ProducerConsumer() { } public void start() { new Producer().start(); new Comsumer().start(); } class Producer extends Thread { public void run() { while (true) { synchronized (storeHouse) { try { while (storeHouse.size() == MAX) { System.out.println("storeHouse is full , please wait"); storeHouse.wait(); } Object newOb = new Object(); if (storeHouse.add(newOb)) { System.out.println("Producer put a Object to storeHouse"); Thread.sleep((long) (Math.random() * 3000)); storeHouse.notify(); } } catch (InterruptedException ie) { System.out.println("producer is interrupted!"); } } } } } class Comsumer extends Thread { public void run() { while (true) { synchronized (storeHouse) { try { while (storeHouse.size() == 0) { System.out.println("storeHouse is empty , please wait"); storeHouse.wait(); } storeHouse.removeLast(); System.out.println("Comsumer get a Object from storeHouse"); Thread.sleep((long) (Math.random() * 3000)); storeHouse.notify(); } catch (InterruptedException ie) { System.out.println("Consumer is interrupted"); } } } } } public static void main(String[] args) throws Exception { ProducerConsumer pc = new ProducerConsumer(); pc.start(); } }
2、await()和signal(),即线程锁的方式
package sort; import java.util.LinkedList; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ProducerConsumer { private LinkedList<Object> myList = new LinkedList<Object>(); private int MAX = 10; private final Lock lock = new ReentrantLock(); private final Condition full = lock.newCondition(); private final Condition empty = lock.newCondition(); public ProducerConsumer() { } public void start() { new Producer().start(); new Consumer().start(); } public static void main(String[] args) throws Exception { ProducerConsumer s2 = new ProducerConsumer(); s2.start(); } class Producer extends Thread { public void run() { while (true) { lock.lock(); try { while (myList.size() == MAX) { System.out.println("warning: it's full!"); full.await(); } Object o = new Object(); if (myList.add(o)) { System.out.println("Producer: " + o); empty.signal(); } } catch (InterruptedException ie) { System.out.println("producer is interrupted!"); } finally { lock.unlock(); } } } } class Consumer extends Thread { public void run() { while (true) { lock.lock(); try { while (myList.size() == 0) { System.out.println("warning: it's empty!"); empty.await(); } Object o = myList.removeLast(); System.out.println("Consumer: " + o); full.signal(); } catch (InterruptedException ie) { System.out.println("consumer is interrupted!"); } finally { lock.unlock(); } } } } }
3、阻塞队列的方式
import java.util.concurrent.*; public class ProducerConsumer { // 建立一个阻塞队列 private LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<Object>(10); public ProducerConsumer() { } public void start() { new Producer().start(); new Consumer().start(); } public static void main(String[] args) throws Exception { ProducerConsumer s3 = new ProducerConsumer(); s3.start(); } class Producer extends Thread { public void run() { while (true) { try { Object o = new Object(); // 取出一个对象 queue.put(o); System.out.println("Producer: " + o); } catch (InterruptedException e) { System.out.println("producer is interrupted!"); } // } } } } class Consumer extends Thread { public void run() { while (true) { try { // 取出一个对象 Object o = queue.take(); System.out.println("Consumer: " + o); } catch (InterruptedException e) { System.out.println("producer is interrupted!"); } // } } } } }
三种方式原理一致,都是对独占空间加锁,阻塞和唤醒线程,第一种方式比较传统,第三种方式最简单,只需存储和取用,线程同步的操作交由LinkedBlockingQueue全权处理。
wait()和notify()其他例子
产品类
//生产的产品 public class Product { private int productId; public Product(int productId) { this.productId = productId; } public int getProductId() { return productId; } public void setProductId(int productId) { this.productId = productId; } @Override public String toString() { return "product_"+productId; } }
仓库类
import java.util.ArrayList; import java.util.List; public class StoreHouse { private static final Integer initialSize = 10; private List<Product> list = null; public StoreHouse() { this.list = new ArrayList<Product>(initialSize); } public synchronized void put(Product product) { if (list.size() == StoreHouse.initialSize) { try { System.out.println("仓库已满,等待消费者消费"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } list.add(product); System.out.println("放入仓库" + product); notifyAll(); } public synchronized void get() { if (list.size() == 0) { try { System.out.println("仓库为空,等待生产者生产"); this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Product product = list.get(list.size()-1); System.out.println("从仓库取出" + product); list.remove(list.size()-1); notifyAll(); } }
生产者
public class Producer implements Runnable { StoreHouse storeHouse = null; public Producer(StoreHouse storeHouse) { this.storeHouse = storeHouse; } @Override public void run() { int i = 0; while (true) { Product product = new Product(++i); storeHouse.put(product); try { Thread.sleep(1000); } catch (InterruptedException e) { return; } } } }
消费者
public class Consumer implements Runnable { StoreHouse storeHouse = null; public Consumer(StoreHouse storeHouse) { this.storeHouse = storeHouse; } @Override public void run() { int i = 0; while (true) { storeHouse.get(); try { Thread.sleep(1500); } catch (InterruptedException e) { return; } } } }
客户端测试
public class Client { public static void main(String[] args) { StoreHouse storeHouse = new StoreHouse(); Runnable producer = new Producer(storeHouse); Runnable consumer = new Consumer(storeHouse); Thread produceThread = new Thread(producer); Thread consumeThread = new Thread(consumer); produceThread.start(); consumeThread.start(); } }