生产消费者模式
生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。
示意图:
实现方式
实现的核心
保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。
Java能实现的几种方法
- wait() / notify()方法
- await() / signal()方法
- BlockingQueue阻塞队列方法
- 信号量(待更新)
Container.java
package com.mars.sample;
public interface Container<E> {
public E get();
public void put(E e);
public int capacity();
}
1、wait() / notify()方式
package com.mars.sample;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
public class WaitContainer<E> implements Container<E> {
private LinkedList<E> queue;
private int capacity;
public WaitContainer(int capacity) {
this.queue = new LinkedList<E>();
this.capacity = capacity;
}
public boolean isFull() {
return queue.size() >= capacity();
}
public boolean isEmpty() {
return queue.size() == 0;
}
public E get() {
E val = null;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized (queue) {
while (isEmpty()) {
try {
System.out.println("container is empty,waiting ...");
queue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
val = queue.poll();
queue.notify();
}
return val;
}
public void put(E e) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
synchronized (queue) {
while (isFull()) {
try {
System.out.println("container is Full,waiting ...,size="+queue.size());
queue.wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
queue.add(e);
queue.notify();
}
}
@Override
public int capacity() {
return capacity;
}
}
2、 await() / signal()方式
package com.mars.sample;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionContainer<E> implements Container<E> {
private LinkedList<E> queue;
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public ConditionContainer(int capacity) {
this.queue = new LinkedList<E>();
this.capacity = capacity;
}
@Override
public E get() {
// 消费
E val = null;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
while (isEmpty()) {
try {
System.out.println("container is empty,waiting ...");
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
val = queue.poll();
notFull.signal();
} finally {
lock.unlock();
}
return val;
}
@Override
public void put(E e) {
// 生产
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
lock.lock();
try {
while (isFull()) {
try {
System.out.println("container is Full,waiting ...,size="+queue.size());
notFull.await();
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
queue.add(e);
notEmpty.signal();
} finally {
lock.unlock();
}
}
@Override
public int capacity() {
return capacity;
}
public boolean isFull() {
return queue.size() >= capacity();
}
public boolean isEmpty() {
return queue.size() == 0;
}
}
3、BlockingQueue阻塞队列方式
package com.mars.sample;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlockingContainer<E> implements Container<E> {
private BlockingQueue<E> queue;
private int capacity;
public BlockingContainer(int capacity) {
this.queue = new ArrayBlockingQueue<E>(capacity);
this.capacity = capacity;
}
@Override
public E get() {
E val = null;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
val = queue.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return val;
}
@Override
public void put(E e) {
try {
queue.put(e);
System.out.println("container size="+queue.size());
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
@Override
public int capacity() {
return capacity;
}
}
生产者
package com.mars.sample;
import java.util.Random;
public class Producer implements Runnable {
private Container<Integer> container;
public Producer(Container<Integer> container) {
this.container = container;
}
@Override
public void run() {
while(true) {
Integer val = new Random().nextInt(100);
container.put(val);
System.out.println("Producer--" + Thread.currentThread().getName() + "--put:" + val);
}
}
}
消费者
package com.mars.sample;
public class Consumer implements Runnable {
private Container<Integer> container;
public Consumer(Container<Integer> container) {
this.container = container;
}
@Override
public void run() {
while(true) {
Integer val = container.get();
System.out.println("consumer--" + Thread.currentThread().getName() + "--take:" + val);
}
}
}
主函数
package com.mars.sample;
public class Main {
public static void main(String[] args) {
//wait() / notify()方式
Container<Integer> container = new WaitContainer<>(5);
for (int i = 0; i < 5; i++) {
new Thread(new Producer(container), "ProducerWait").start();
}
for (int i = 0; i < 2; i++) {
new Thread(new Consumer(container), "ConsumerWait").start();
}
//await() / signal()方式
container = new ConditionContainer<>(5);
for (int i = 0; i < 5; i++) {
new Thread(new Producer(container), "ProducerCondition").start();
}
for (int i = 0; i < 2; i++) {
new Thread(new Consumer(container), "ConsumerCondition").start();
}
//BlockingQueue阻塞队列方式
container = new BlockingContainer<>(5);
for (int i = 0; i < 5; i++) {
new Thread(new Producer(container), "ProducerBlocking").start();
}
for (int i = 0; i < 2; i++) {
new Thread(new Consumer(container), "ConsumerBlocking").start();
}
}
}