什么是消费者-生产者模式
简单来说,就是存在两个线程,公用一块共享区域,但是需要特别说明的是:
- 当共享区域满时,生产者线程必须等待(阻塞);
- 当共享区域空时,消费者线程必须等待(阻塞)。
比如当我们去饭店吃饭的时候,这个时候厨师为生产者,客人为消费者。当没有客人的时候,厨师需要等待有客人才能做菜;当客满的时候,客人必须等待。
实现方式
Object的wait/notify消息通知
- wait:将当前线程置为休眠状态,知道接到通知或被中断;
- notify:从waiting状态的线程中,挑选一个线程进行通知(唤醒),将这个线程从等待队列移入同步队列。
- notifyall:唤醒所有waiting线程,并移入到同步队列中。
class Test2{
LinkedList<Integer> linkedList = new LinkedList<>();
int size = 10;
ExecutorService executorService = Executors.newFixedThreadPool(15);
for(int i = 0; i < 5; i++){
executorService.submit(new Product(linkedList, size));
}
for(int i = 0; i < 10; i++){
executorService.submit(new Customer(linkedList));
}
executorService.shutdown();
}
static class Product extends Thread{
private LinkedList<Integer> linkedList;
private int size;
Product(LinkedList<Integer> linkedList, int size){
this.linkedList = linkedList;
this.size = size;
}
@Override
public void run() {
while (true){
synchronized (linkedList){
try {
while(linkedList.size() == 9){
System.out.println(Thread.currentThread().getName() + "共享队列已满, 生产者开始等待......");
linkedList.wait();
System.out.println(Thread.currentThread().getName() + ", 生产者等待完毕,开始生产");
}
int idx = new Random().nextInt();
System.out.println(Thread.currentThread().getName() + "产品id:" + idx);
linkedList.add(idx);
linkedList.notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
static class Customer extends Thread{
private LinkedList<Integer> linkedList;
Customer(LinkedList<Integer> linkedList){
this.linkedList = linkedList;
}
@Override
public void run() {
while (true){
synchronized (linkedList){
try {
while (linkedList.isEmpty()){
System.out.println(Thread.currentThread().getName() + "共享队列为空, 消费者开始等待......");
linkedList.wait();
System.out.println(Thread.currentThread().getName() + "消费者开始消费......");
}
int tmp = linkedList.removeLast();
System.out.println(Thread.currentThread().getName() + "消费者账单id:" + tmp);
linkedList.notifyAll();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
Lock的condition的await/signal
与object用法类似,不同的地方在于使用condition之前需要取得锁
class Test3{
static ReentrantLock lock = new ReentrantLock();
static Condition full = lock.newCondition();
static Condition empty = lock.newCondition();
public static void main(String[] args) {
LinkedList<Integer> linkedList = new LinkedList<>();
ExecutorService executorService = Executors.newFixedThreadPool(15);
for(int i = 0; i < 8; i++){
executorService.submit(new Product(linkedList, 10, lock));
}
for(int i = 0; i < 12; i++){
executorService.submit(new Customer(linkedList, lock));
}
executorService.shutdown();
}
static class Product implements Runnable {
LinkedList<Integer> linkedList;
int size;
Lock lock;
Product(LinkedList<Integer> linkedList, int size, Lock lock){
this.linkedList = linkedList;
this.size = size;
this.lock = lock;
}
@Override
public void run() {
while (true){
lock.lock();
try {
while (linkedList.size() == size){
System.out.println(Thread.currentThread().getName() + "共享队列已满,生产者开始等待...");
full.await();
System.out.println(Thread.currentThread().getName() + "生产者等待完毕...");
}
int idx = new Random().nextInt();
linkedList.add(idx);
System.out.println(Thread.currentThread().getName() + "生产产品id:" + idx);
empty.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
static class Customer implements Runnable{
LinkedList<Integer> linkedList;
int size;
Lock lock;
Customer(LinkedList<Integer> linkedList, Lock lock){
this.linkedList = linkedList;
this.lock = lock;
}
@Override
public void run() {
while (true){
lock.lock();
try {
while (linkedList.isEmpty()){
System.out.println(Thread.currentThread().getName() + "共享队列为空,消费开始等待...");
empty.await();
System.out.println(Thread.currentThread().getName() + "生消费者等待完毕...");
}
int idx = linkedList.removeLast();
System.out.println(Thread.currentThread().getName() + "消费产品id:" + idx);
full.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
}
基于BlockingQueue
BlockingQueue 只不过是在put和pop方法里面添加了基于condition的锁
class Test4{
static LinkedBlockingQueue<Integer> linkedBlockingDeque = new LinkedBlockingQueue<>();
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(15);
for(int i = 0; i < 8; i++){
executorService.submit(new Product(linkedBlockingDeque));
}
for(int i = 0; i < 12; i++){
executorService.submit(new Customer(linkedBlockingDeque));
}
executorService.shutdown();
}
static class Product implements Runnable{
LinkedBlockingQueue<Integer> blockingQueue;
Product(LinkedBlockingQueue<Integer> blockingQueue){
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
while (true){
try {
int idx = new Random().nextInt();
blockingQueue.put(idx);
System.out.println(Thread.currentThread().getName() + "生产产品id:" + idx);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
static class Customer implements Runnable{
LinkedBlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<>();
Customer(LinkedBlockingQueue<Integer> blockingQueue){
this.blockingQueue = blockingQueue;
}
@Override
public void run() {
while (true){
try {
int idx = blockingQueue.take();
System.out.println(Thread.currentThread().getName() + "消费产品id:" + idx);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
refrence
- 每个Condition对象都包含一个等待队列
- 在Object的监视器模型上,一个对象拥有一个同步队列与一个等待队列,而AQS拥有一个同步队列和多个等待队列。
- 调用condition的await方法,将会使当前线程进入等待队列并释放锁(先加入等待队列再释放锁),同时线程状态转为等待状态。
- 调用condition的signal方法时,将会把等待队列的首节点移到等待队列的尾部,然后唤醒该节点。被唤醒,并不代表就会从await方法返回,也不代表该节点的线程能获取到锁,它一样需要加入到锁的竞争acquireQueued方法中去,只有成功竞争到锁,才能从await方法返回。