所谓生产者消费者模式,即N个线程进行生产,同时N个线程进行消费,两种角色通过内存缓冲区进行通信,
为什么要使用这种模式进行生产代码。
优点:
极大的解决了代码之间的耦合程度
解释:之前我们写的代码可能是这样的,有A和B 两个功能代码处理数据,B代码的执行必须要依赖于A代码才能执行完B的功能,即A耦合于B。显然这严重影响B代码的生产,而且如果产生错误的话,也不容易及时的排查。采用生产者消费者模式,A代码将处理好的数据交给缓存区,B代码直接从缓存区拿取数据进行处理,这样就把A与B的依赖关系给简介的消除了。
Object的wait() / notify()方法
public class consumer {
public static void main(String[] args) {
stock s = new stock();
Get get = new Get(s);
Put put = new Put(s);
Get get1 = new Get(s);
Put put1 = new Put(s);
//多个生产者和消费者对缓存区进行拿放数据
get.start();
put.start();
get1.start();
put1.start();
}
}
//缓存区
class stock{
private int maxnum=10;//最大的容量通知消费者
private int minnum=0;//最小的容量通知生产者
private String name;//定义仓库中存在的内容
private ArrayList contain = new ArrayList<>();//存储的列表
//向里面放入东西
public synchronized void putOne(String name){
while(contain.size()>maxnum){//如果仓库中的容量大于最大容量进行线程等待
try {
this.wait();//线程等待
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
contain.add(name);
System.out.println("生产者生产了"+name+"消费者赶紧来消费"+Thread.currentThread().getName());
this.notifyAll();//唤起所有线程
}
public synchronized void getOne(){
while(contain.size()<=minnum){//如果仓库没有低于最低容量
try {
this.wait();//线程等待 等待生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("消费者消费了"+contain.get(0)+"生产者赶紧生产"+Thread.currentThread().getName());
contain.remove(0);
this.notifyAll();//唤起
}
}
//拿
class Get extends Thread{
private stock s;//缓存区
public Get(stock s) {
this.s = s;
}
@Override
public synchronized void run() {
while(true){
s.getOne();
}
}
}
//放
class Put extends Thread{
private stock s;//缓存区
public Put(stock s) {
this.s = s;
}
@Override
public synchronized void run() {
while(true){
s.putOne("mac");
}
}
}
使用Lock和Condition的await() / signal()方法
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @auther sofency
* @date 2019/11/29 21:16
* @package 设计模式.lock
* 使用锁进行控制 lock
*/
public class lockTest {
public static void main(String[] args) {
Queue queue = new LinkedBlockingQueue<>(100);
Lock put = new ReentrantLock();//生产者的锁
Lock get = new ReentrantLock();//消费者的锁
Condition noFull=put.newCondition();
Condition noEmpty = get.newCondition();
new Thread(new producer(queue,"alice0",put,get,60,noFull,noEmpty)).start();
new Thread(new producer(queue,"alice1",put,get,60,noFull,noEmpty)).start();
new Thread(new producer(queue,"alice2",put,get,60,noFull,noEmpty)).start();
new Thread(new consumer(queue,"sophia0",put,get,0,noFull,noEmpty)).start();
new Thread(new consumer(queue,"sophia1",put,get,0,noFull,noEmpty)).start();
new Thread(new consumer(queue,"sophia2",put,get,0,noFull,noEmpty)).start();
}
}
//生产者
class producer extends Thread{
private Queue queue;//传进来的是带容量的对列
private String name;//线程的名字设置
private Lock put;//生产锁
private Lock get;//消费锁
private int maxSize;//最大容量
private Condition noFull;
private Condition noEmpty;
public producer(Queue queue, String name, Lock put, Lock get, int maxSize, Condition noFull, Condition noEmpty) {
this.queue = queue;
this.name = name;
this.put = put;
this.get = get;
this.maxSize = maxSize;
this.noFull = noFull;
this.noEmpty = noEmpty;
}
@Override
public void run() {
while(true){
put.lock();//上锁
while(queue.size()==maxSize){
try {
System.out.println("对列已满 通知消费者进行消费");
noFull.await();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep((int)Math.random()*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.add("mac");//添加物品
System.out.println(name+"向对列里面添加mac"+queue.size());
noFull.signalAll();//唤醒其他生产者
put.unlock();
if(queue.size()==maxSize){
get.lock();
noEmpty.signalAll();
get.unlock();
}
}
}
}
//消费者
class consumer extends Thread{
private Queue queue;//传进来的是带容量的对列
private String name;//线程的名字设置
private Lock put;//生产锁
private Lock get;//消费锁
private int minnum;//最大容量
private Condition noFull;
private Condition noEmpty;
public consumer(Queue queue, String name, Lock put, Lock get, int minnum, Condition noFull, Condition noEmpty) {
this.queue = queue;
this.name = name;
this.put = put;
this.get = get;
this.minnum = minnum;
this.noFull = noFull;
this.noEmpty = noEmpty;
}
@Override
public void run() {
while(true){
get.lock();//上锁
while(queue.size()==minnum){
try {
System.out.println("对列快空了 通知生产者进行生产");
noEmpty.await();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep((int)Math.random()*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.remove();//拿取第一个物品
System.out.println(name+"向对列里面消费了mac"+queue.size());
if(queue.size()>minnum){
noEmpty.signal();//唤醒其他的消费者
}
get.unlock();
if(queue.size()==minnum){
put.lock();
noFull.signalAll();
put.unlock();
}
}
}
}
使用BlockingQueue阻塞队列方法
它在底层的实现方法中就已经进行同步操作了,因此我们再用生产者和消费者进行操作的时候不用再进行同步的操作, 具体的代码如下。
注意
a. LinkedBlockingDeque类中的put方法根据容量进行自动阻塞, 即容量超了之后自动进行等待并通知消费者进行消费
b. LinkedBlockingDeque类中的take()方法根据容量进行自动阻塞, 即容量为0后自动进行等待并通知生产者进行生产
public class block {
public static void main(String[] args) {
LinkedBlockingDeque link = new LinkedBlockingDeque<>(8);//指明容量
new Get(link,"alice").start();
new Put(link,"sofency").start();
}
}
//消费者
class Get extends Thread{
private LinkedBlockingDeque blockingDeque;
private String name;//线程的名字
public Get(LinkedBlockingDeque blockingDeque, String name) {
this.blockingDeque = blockingDeque;
this.name = name;
}
@Override
public void run() {
while(true){
try {
String str = blockingDeque.take();//超过自动阻塞
System.out.println(name + "向对列里面拿了"+str);
Thread.sleep(new Random().nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者
class Put extends Thread{
private LinkedBlockingDeque blockingDeque;
private String name;//线程的名字
public Put(LinkedBlockingDeque blockingDeque, String name) {
this.blockingDeque = blockingDeque;
this.name = name;
}
@Override
public void run() {
while(true){
try {
blockingDeque.put("mac");//超过自动阻塞
System.out.println(name + "向对列里面添加了mac");
Thread.sleep(new Random().nextInt(1000));
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
效果如下