什么是生产者与消费者
生产者消费者模式是一个十分经典的多线程协作模式
因为多线程的随机性,导致线程A与线程B会出现都是线程A在运作或者都是线程B在运作。而等待唤醒机制可以让线程A和线程B轮流运行。
对应到下面的线程如下:
代码实现
代码示例
package com.ruoyi.web.controller.thread;
/**
* 生产者
*
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class Producer1 extends Thread {
public Producer1() {
}
public Producer1(String name) {
super(name);
}
@Override
public void run() {
/**
* 1. 循环
* 2. 同步代码块/同步方法
* 3. 判断共享数据是否到了末尾(到了末尾)
* 4. 判断共享数据是否到了末尾(未到末尾,执行核心逻辑)
*
*/
while (true){
synchronized (Control1.lock){
if(Control1.count == 0){
break;
}else {
//判断数据池 是否有数据
if(Control1.controlFlag == 1){
//如果有就等待
try {
//需要用锁对象来调用等待
Control1.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//没有就要生产
System.out.println("生产者生产了一条数据!");
//修改控制着状态
Control1.controlFlag = 1;
//生产完成 唤醒消费者消费
Control1.lock.notifyAll();//唤醒跟这把锁绑定的所有线程
}
}
}
}
}
}
package com.ruoyi.web.controller.thread;
/**
* 消费者
*
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class Consumer1 extends Thread {
public Consumer1() {
}
public Consumer1(String name) {
super(name);
}
@Override
public void run() {
/**
* 1. 循环
* 2. 同步代码块/同步方法
* 3. 判断共享数据是否到了末尾(到了末尾)
* 4. 判断共享数据是否到了末尾(未到末尾,执行核心逻辑)
*
*/
while (true){
synchronized (Control1.lock){
if(Control1.count == 0){
break;
}else {
//判断数据池 是否有数据
if(Control1.controlFlag == 0){
//如果没有就等待
try {
//需要用锁对象来调用等待
Control1.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//消费者完成后 把线程池总量-1
Control1.count--;
//有就消费
System.out.println("消费者正在一条消费数据,还剩下"+ Control1.count+"条数据未消费!");
//消费完成 唤醒生产者生产
Control1.lock.notifyAll();//唤醒跟这把锁绑定的所有线程
//修改控制着状态
Control1.controlFlag = 0;
}
}
}
}
}
}
package com.ruoyi.web.controller.thread;
/**
* 中间控制着 控制消费者和生产者的
*
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class Control1 {
//控制者状态 是否有数据 0 没有 1 有数据 为啥此刻不用boolean 因为可能会出现多条线程
public static int controlFlag = 0;
//控制者 总的数据量
public static int count = 10;
//锁对象
public static Object lock = new Object();
public static void main(String[] args) {
//创建线程A B
Consumer1 consumer1 = new Consumer1("线程A");
Producer1 producer1 = new Producer1("线程B");
//开启线程A B
consumer1.start();
producer1.start();
}
}
升级优化:(阻塞队列方式实现)
put数据时:放不进去,会等着-->阻塞
take数据时:去除第一个数据,取不到会等着-->阻塞
阻塞队列的继承结构:
代码实现
package com.ruoyi.web.controller.thread;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 生产者
*
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class Producer2 extends Thread {
ArrayBlockingQueue<String> queue;
public Producer2() {
}
public Producer2(String name, ArrayBlockingQueue<String> queue) {
super(name);
this.queue = queue;
}
@Override
public void run() {
while (true) {
//不断将数据放入队列 put中加了锁 所以我们不需要加锁
try {
queue.put("数据!!");
System.out.println("生产者生产数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package com.ruoyi.web.controller.thread;
import java.util.concurrent.ArrayBlockingQueue;
/**
* 消费者
*
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class Consumer2 extends Thread {
ArrayBlockingQueue<String> queue;
public Consumer2() {
}
public Consumer2(String name, ArrayBlockingQueue<String> queue) {
super(name);
this.queue = queue;
}
@Override
public void run() {
/**
* 1. 循环
* 2. 同步代码块/同步方法
* 3. 判断共享数据是否到了末尾(到了末尾)
* 4. 判断共享数据是否到了末尾(未到末尾,执行核心逻辑)
*
*/
while (true){
while (true) {
//不断将数据从队列去除 take中加了锁 所以我们不需要加锁
try {
String take = queue.take();
System.out.println("消费者消费了数据:"+take);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
package com.ruoyi.web.controller.thread;
import java.util.concurrent.ArrayBlockingQueue;
/**
*
*
* @author zhuang.bq
* @create 2024/5/16 14:53
* @desc
**/
public class Control2 {
public static void main(String[] args) {
/**
* 需求:利用阻塞队列完成生成者和消费者(等待唤醒机制)的代码
* 细节:
* 生产者和消费者必须使用同一个阻塞队列
*/
//1.创建阻塞对象
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1 );
//创建线程A B
Consumer2 consumer2 = new Consumer2("线程A",queue);
Producer2 producer2 = new Producer2("线程B",queue);
//开启线程A B
consumer2.start();
producer2.start();
}
}