一、简介
生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此并不直接通信,而是通过阻塞队列进行通信,所以生产者生产完数据后不用等待消费者进行处理,而是直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列中获取数据,阻塞队列就相当于一个缓冲区,平衡生产者和消费者的处理能力。
二、实现方式
1、synchronized、wait和notify
生产者和消费者线程各一条:
代码实现:
package com.thread.producerAndConsumer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @author riemann
* @date 2019/07/20 0:13
*/
public class ProducerAndConsumer {
public static void main(String[] args) {
List list = new ArrayList();
new Thread((new Producer(list)),"生产者线程produce: ").start();
new Thread((new Consumer(list)),"消费者线程consume: ").start();
}
}
/**
* 生产者线程
*/
class Producer implements Runnable {
private List list;
public Producer(List list) {
this.list = list;
}
@Override
public void run() {
while (true) {
Random random = new Random();
synchronized (list) {
//可以是while
if (list.size() > 0) {//表明集合中有元素,此线程等待
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(random.nextInt(100));//0-99的随机数
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + list.get(0));
list.notify();//通知消费者,集合中已有元素。
}
}
}
}
/**
* 消费者线程
*/
class Consumer implements Runnable {
private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
while (true) {
synchronized (list) {
//可以是while
if (list.size() < 1) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + list.remove(0));
list.notify();
}
}
}
}
输出结果:
生产者线程produce: 40
消费者线程consume: 40
生产者线程produce: 9
消费者线程consume: 9
生产者线程produce: 95
消费者线程consume: 95
生产者线程produce: 50
消费者线程consume: 50
生产者线程produce: 33
消费者线程consume: 33
生产者线程produce: 16
消费者线程consume: 16
生产者线程produce: 65
消费者线程consume: 65
生产者线程produce: 87
消费者线程consume: 87
生产者线程produce: 37
消费者线程consume: 37
生产者线程produce: 34
消费者线程consume: 34
生产者线程produce: 33
消费者线程consume: 33
多条生产者和消费者线程:
多条线程需要注意是:
- 我们唤醒的时候需要使用notifyAll(),如果使用notify()随机唤醒的可能是同一类线程,这样会导致死锁;
- 需要将if改为while,比如生产者线程有多个,当本生产者线程wait之后,假如另一个生产者线程得到锁(本该消费者得到),如果是if,那么此线程就会继续执行,会导致数据错乱。如果是while则会继续等待。
代码实现:
package com.thread.producerAndConsumer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* @author riemann
* @date 2019/07/20 0:52
*/
public class ProducerAndConsumer2 {
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 4; i++) {
new Thread((new Producer(list)),"生产者线程" + i +" - produce: ").start();
new Thread((new Consumer(list)),"消费者线程" + i +" - consume: ").start();
}
}
}
/**
* 生产者线程
*/
class Producer implements Runnable {
private List list;
public Producer(List list) {
this.list = list;
}
@Override
public void run() {
while (true) {
Random random = new Random();
synchronized (list) {
while (list.size() > 0) {
//因为生产者线程有多个,当本线程wait之后,假如一个生产者线程得到锁(本该消费者得到),
// 如果是if,那么此线程就会继续执行,会导致数据错乱。
//如果是while则会继续等待。
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.add(random.nextInt(100));//0-99的随机数
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + list.get(0));
list.notifyAll();//唤醒此对象锁所有等待线程(消费者和生产者线程均有)
}
}
}
}
/**
* 消费者线程
*/
class Consumer implements Runnable {
private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
while (true) {
synchronized (list) {
while (list.size() < 1) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + list.remove(0));
list.notifyAll();//唤醒此对象锁所有等待线程(消费者和生产者线程均有)
}
}
}
}
输出结果:
生产者线程0 - produce: 83
消费者线程3 - consume: 83
生产者线程3 - produce: 32
消费者线程0 - consume: 32
生产者线程2 - produce: 24
消费者线程0 - consume: 24
生产者线程3 - produce: 55
消费者线程2 - consume: 55
生产者线程0 - produce: 98
消费者线程3 - consume: 98
生产者线程3 - produce: 52
消费者线程0 - consume: 52
生产者线程1 - produce: 8
消费者线程0 - consume: 8
生产者线程3 - produce: 52
消费者线程2 - consume: 52
2、ReentrantLock 和 BlockingQueue
采用两把锁,实现生产者和消费者同时作业。需要注意的是,生产者的一个锁对象,消费者一个锁对象。分别用来唤醒消费者和生产者。
代码实现:
多条生产者和消费者随机交替,也就是说只要队列没有满那一直生产,只要队列没有空那就一直消费。
package com.thread.producerAndConsumer;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author riemann
* @date 2019/07/20 1:17
*/
public class ProducerAndConsumer3 {
public static void main(String[] args) {
LinkedBlockingDeque<Integer> queue = new LinkedBlockingDeque<>();
ReentrantLock put = new ReentrantLock();
Condition notFull = put.newCondition();
ReentrantLock out = new ReentrantLock();
Condition notEmpty = out.newCondition();
for (int i = 0; i < 4; i++) {
new Thread(new Producer(put, out ,notFull, notEmpty, queue),"生产者线程" + i +" - produce: ").start();
new Thread(new Consumer(put, out ,notFull, notEmpty, queue),"消费者线程" + i +" - consume: ").start();
}
}
}
/**
* 生产者
*/
class Producer implements Runnable {
ReentrantLock put;
ReentrantLock out;
Condition notFull;
Condition notEmpty;
Queue<Integer> queue;
public Producer(ReentrantLock put, ReentrantLock out, Condition notFull, Condition notEmpty, Queue<Integer> queue) {
this.put = put;
this.out = out;
this.notFull = notFull;
this.notEmpty = notEmpty;
this.queue = queue;
}
@Override
public void run() {
Random random = new Random();
while (true) {
put.lock();
while (queue.size() == 10) {
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer integer = random.nextInt(100);
queue.add(integer);
System.out.println(Thread.currentThread().getName() + " " + integer +", 当前Queue还剩" + queue.size());
//队列没有满,通知更多生产者来生产
if (queue.size() < 10) {
notFull.signal();
}
put.unlock();
//只要生产出一个就通知消费者消费,后续不需要通知
//因为消费者内部有唤醒更多消费者的机制
if (queue.size() == 1) {
out.lock();
notEmpty.signal();
out.unlock();
}
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable {
ReentrantLock put;
ReentrantLock out;
Condition notFull;
Condition notEmpty;
Queue<Integer> queue;
public Consumer(ReentrantLock put, ReentrantLock out, Condition notFull, Condition notEmpty, Queue queue){
this.put=put;
this.out=out;
this.notFull=notFull;
this.notEmpty=notEmpty;
this.queue=queue;
}
@Override
public void run() {
while (true) {
out.lock();
while (queue.size() == 0) {
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "+ queue.poll() + ", " + "当前Queue还剩" + queue.size());
//只要队列里还有元素,就通知更多消费者来消费
if (queue.size() > 0) {
notEmpty.signal();
}
out.unlock();
//只要队列没有满,就通知生产者进行生产
if (queue.size() == 9) {
put.lock();
notFull.signal();
put.unlock();
}
}
}
}
输出结果:
生产者线程0 - produce: 31, 当前Queue还剩1
生产者线程1 - produce: 28, 当前Queue还剩2
消费者线程0 - consume: 31, 当前Queue还剩1
生产者线程3 - produce: 40, 当前Queue还剩2
消费者线程0 - consume: 28, 当前Queue还剩1
生产者线程3 - produce: 97, 当前Queue还剩2
消费者线程0 - consume: 40, 当前Queue还剩1
生产者线程2 - produce: 28, 当前Queue还剩1
消费者线程0 - consume: 97, 当前Queue还剩1
消费者线程0 - consume: 28, 当前Queue还剩1
生产者线程0 - produce: 13, 当前Queue还剩1
消费者线程0 - consume: 13, 当前Queue还剩1
生产者线程1 - produce: 32, 当前Queue还剩1
生产者线程1 - produce: 77, 当前Queue还剩2
消费者线程2 - consume: 32, 当前Queue还剩1
消费者线程2 - consume: 77, 当前Queue还剩1
生产者线程1 - produce: 99, 当前Queue还剩1
消费者线程1 - consume: 99, 当前Queue还剩0
生产者线程3 - produce: 38, 当前Queue还剩1
生产者线程2 - produce: 12, 当前Queue还剩1
消费者线程2 - consume: 38, 当前Queue还剩1
多条生产者和消费者,实现生产满了在消费,消费完了在生产。
package com.thread.producerAndConsumer;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author riemann
* @date 2019/07/20 1:59
*/
public class ProducerAndConsumer4 {
public static void main(String[] args) {
LinkedBlockingDeque<Integer> queue = new LinkedBlockingDeque<>();
ReentrantLock put = new ReentrantLock();
Condition notFull = put.newCondition();
ReentrantLock out = new ReentrantLock();
Condition notEmpty = out.newCondition();
for (int i = 0; i < 4; i++) {
new Thread(new Producer(put, out ,notFull, notEmpty, queue),"生产者线程" + i +" - produce: ").start();
new Thread(new Consumer(put, out ,notFull, notEmpty, queue),"消费者线程" + i +" - consume: ").start();
}
}
}
/**
* 生产者
*/
class Producer implements Runnable {
ReentrantLock put;
ReentrantLock out;
Condition notFull;
Condition notEmpty;
Queue<Integer> queue;
public Producer(ReentrantLock put, ReentrantLock out, Condition notFull, Condition notEmpty, Queue<Integer> queue) {
this.put = put;
this.out = out;
this.notFull = notFull;
this.notEmpty = notEmpty;
this.queue = queue;
}
@Override
public void run() {
Random random = new Random();
while (true) {
put.lock();
while (queue.size() == 10) {
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer integer = random.nextInt(100);
queue.add(integer);
System.out.println(Thread.currentThread().getName() + " " + integer +", 当前Queue还剩" + queue.size());
//队列没有满,通知更多生产者来生产
if (queue.size() < 10) {
notFull.signal();
}
put.unlock();
// 当队列已满时才通知消费者进行消费
if (queue.size() == 10) {
out.lock();
notEmpty.signal();
out.unlock();
}
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable {
ReentrantLock put;
ReentrantLock out;
Condition notFull;
Condition notEmpty;
Queue<Integer> queue;
public Consumer(ReentrantLock put, ReentrantLock out, Condition notFull, Condition notEmpty, Queue queue){
this.put=put;
this.out=out;
this.notFull=notFull;
this.notEmpty=notEmpty;
this.queue=queue;
}
@Override
public void run() {
while (true) {
out.lock();
while (queue.size() == 0) {
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "+ queue.poll() + ", " + "当前Queue还剩" + queue.size());
//只要队列里还有元素,就通知更多消费者来消费
if (queue.size() > 0) {
notEmpty.signal();
}
out.unlock();
//当队列为空时,通知生产者进行生产
if (queue.size() == 0) {
put.lock();
notFull.signal();
put.unlock();
}
}
}
}
输出结果:
生产者线程0 - produce: 87, 当前Queue还剩1
生产者线程0 - produce: 62, 当前Queue还剩2
生产者线程0 - produce: 8, 当前Queue还剩3
生产者线程1 - produce: 15, 当前Queue还剩4
生产者线程1 - produce: 40, 当前Queue还剩5
生产者线程2 - produce: 77, 当前Queue还剩6
生产者线程3 - produce: 48, 当前Queue还剩7
生产者线程0 - produce: 81, 当前Queue还剩8
生产者线程1 - produce: 56, 当前Queue还剩9
生产者线程1 - produce: 50, 当前Queue还剩10
消费者线程1 - consume: 87, 当前Queue还剩9
消费者线程1 - consume: 62, 当前Queue还剩8
消费者线程3 - consume: 8, 当前Queue还剩7
消费者线程3 - consume: 15, 当前Queue还剩6
消费者线程3 - consume: 40, 当前Queue还剩5
消费者线程3 - consume: 77, 当前Queue还剩4
消费者线程0 - consume: 48, 当前Queue还剩3
消费者线程0 - consume: 81, 当前Queue还剩2
消费者线程0 - consume: 56, 当前Queue还剩1
消费者线程0 - consume: 50, 当前Queue还剩0
生产者线程1 - produce: 81, 当前Queue还剩1
生产者线程1 - produce: 26, 当前Queue还剩2
生产者线程1 - produce: 72, 当前Queue还剩3
生产者线程1 - produce: 45, 当前Queue还剩4
生产者线程2 - produce: 66, 当前Queue还剩5
生产者线程2 - produce: 96, 当前Queue还剩6
生产者线程2 - produce: 95, 当前Queue还剩7
生产者线程2 - produce: 34, 当前Queue还剩8
生产者线程2 - produce: 65, 当前Queue还剩9
生产者线程2 - produce: 48, 当前Queue还剩10
消费者线程0 - consume: 81, 当前Queue还剩9
消费者线程0 - consume: 26, 当前Queue还剩8
消费者线程0 - consume: 72, 当前Queue还剩7
消费者线程0 - consume: 45, 当前Queue还剩6
消费者线程1 - consume: 66, 当前Queue还剩5
消费者线程1 - consume: 96, 当前Queue还剩4
消费者线程1 - consume: 95, 当前Queue还剩3
消费者线程2 - consume: 34, 当前Queue还剩2
消费者线程2 - consume: 65, 当前Queue还剩1
消费者线程3 - consume: 48, 当前Queue还剩