技术标签: java 并发&JUC 后端
生产者消费者模式是一个十分经典的多线程协作模式
弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻
存在3个元素
1.生产者(类比厨师)
2.生产者的生产产品(类比美食)
3.消费者(类比吃货)
思路分析:
理想情况:
最开始,生产者先抢到cpu执行权;生产出生产产品并放在2者位置之间(类比餐桌)
然后消费者抢到cpu执行权,消费掉生产产品
如此反复循环
生产者和消费者简单来说就是2个线程轮流执行
而实际上线程是随机执行的
下面介绍2个异常的情况:
消费者等待过程:
最开始,消费者先抢到cpu执行权;
现在2者之间还未有生产产品,消费者只能在此等待
然后生产者抢到cpu执行权,生产出生产产品,放在2者之间并通知消费者
消费者得到通知,消费掉生产产品
消费者步骤:
1.判断2者之间是否有生产产品
2.如果没有就等待
生产者步骤:
1.生产出生产产品
2.把生产产品放在2者之间
3.通知等待的消费者进行消费
生产者等待过程:
最开始,生产者先抢到cpu执行权;生产出生产产品并放在2者位置之间
然后还是生产者抢到cpu执行权;由于生产产品还未被消费,自然不再进行生产,从而进行等待
然后消费者抢到cpu执行权,消费掉生产产品
生产者步骤:
1.判断2者之间是否有生产产品;如果有就等待,如果没有才生产
2.把生产产品放在2者之间
3.通知等待的消费者进行消费
消费者步骤:
1.判断2者之间是否有生产产品
2.如果没有就等待
3.如果有就消费掉生产产品,2者之间的生产产品就没有了,通知等待的生产者继续生产,生产产品数量减1
代码实现:
此处生产者是厨师,生产产品是汉堡包,消费者是吃货,2者之间是桌子
//主体
public class Demo {
public static void main(String[] args) {
/*消费者步骤:
1,判断桌子上是否有汉堡包。
2,如果没有就等待。
3,如果有就开吃
4,吃完之后,桌子上的汉堡包就没有了
叫醒等待的生产者继续生产
汉堡包的总数量减一*/
/*生产者步骤:
1,判断桌子上是否有汉堡包
如果有就等待,如果没有才生产。
2,把汉堡包放在桌子上。
3,叫醒等待的消费者开吃。*/
Foodie f = new Foodie();
Cooker c = new Cooker();
f.start();
c.start();
}
}
//生产者(厨师)
public class Cooker extends Thread {
// 生产者步骤:
// 1,判断桌子上是否有汉堡包
// 如果有就等待,如果没有才生产。
// 2,把汉堡包放在桌子上。
// 3,叫醒等待的消费者开吃。
@Override
public void run() {
while(true){
synchronized (Desk.lock){
if(Desk.count == 0){
break;
}else{
if(!Desk.flag){
//生产
System.out.println("厨师正在生产汉堡包");
Desk.flag = true;
Desk.lock.notifyAll();
}else{
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
//2者之间(桌子)
public class Desk {
//定义一个标记
//true 就表示桌子上有汉堡包的,此时允许吃货执行
//false 就表示桌子上没有汉堡包的,此时允许厨师执行
public static boolean flag = false;
//汉堡包的总数量
public static int count = 10;
//锁对象
public static final Object lock = new Object();
}
//消费者(吃货)
public class Foodie extends Thread {
@Override
public void run() {
// 1,判断桌子上是否有汉堡包。
// 2,如果没有就等待。
// 3,如果有就开吃
// 4,吃完之后,桌子上的汉堡包就没有了
// 叫醒等待的生产者继续生产
// 汉堡包的总数量减一
//套路:
//1. while(true)死循环
//2. synchronized 锁,锁对象要唯一
//3. 判断,共享数据是否结束. 结束
//4. 判断,共享数据是否结束. 没有结束
while(true){
synchronized (Desk.lock){
if(Desk.count == 0){
break;
}else{
if(Desk.flag){
//有
System.out.println("吃货在吃汉堡包");
Desk.flag = false;
Desk.lock.notifyAll();
Desk.count--;
}else{
//没有就等待
//使用什么对象当做锁,那么就必须用这个对象去调用等待和唤醒的方法.
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列
这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用
阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素
阻塞队列继承结构:
BlockingQueue的核心方法:
put(anObject):将参数放入队列,如果放不进去会阻塞
take():取出第一个数据,取不到会阻塞
常见的BlockingQueue:
ArrayBlockingQueue:底层是数组,有界
LinkedBlockingQueue:底层是链表,无界;但不是真正的无界,最大为int的最大值
示例代码:
import java.util.concurrent.ArrayBlockingQueue;
public class Main {
public static void main(String[] a) throws Exception{
// 创建阻塞队列的对象,容量为 1
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(1);
// 存储元素
arrayBlockingQueue.put("汉堡包");
// 取元素
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take()); // 取不到会阻塞
System.out.println("程序结束了");//不会进行这段语句,因为上面语句已经导致阻塞了
}
}
案例简要:
生产者类(Cooker):实现Runnable接口,重写run()方法,设置线程任务
1.构造方法中接收一个阻塞队列对象
2.在run方法中循环向阻塞队列中添加包子
3.打印添加结果
消费者类(Foodie):实现Runnable接口,重写run()方法,设置线程任务
1.构造方法中接收一个阻塞队列对象
2.在run方法中循环获取阻塞队列中的包子
3.打印获取结果
测试类(Demo):里面有main方法,main方法中的代码步骤如下
创建阻塞队列对象
创建生产者线程和消费者线程对象,构造方法中传入阻塞队列对象
分别开启两个线程
代码实现:
//主体
public class Demo {
public static void main(String[] args) {
ArrayBlockingQueue<String> bd = new ArrayBlockingQueue<>(1);
Foodie f = new Foodie(bd);
Cooker c = new Cooker(bd);
f.start();
c.start();
}
//生产者(厨师)
public class Cooker extends Thread {
private ArrayBlockingQueue<String> bd;
public Cooker(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
// 生产者步骤:
// 1,判断桌子上是否有汉堡包
// 如果有就等待,如果没有才生产。
// 2,把汉堡包放在桌子上。
// 3,叫醒等待的消费者开吃。
@Override
public void run() {
while (true) {
try {
bd.put("汉堡包");
System.out.println("厨师放入一个汉堡包");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者(吃货)
public class Foodie extends Thread {
private ArrayBlockingQueue<String> bd;
public Foodie(ArrayBlockingQueue<String> bd) {
this.bd = bd;
}
@Override
public void run() {
// 1,判断桌子上是否有汉堡包。
// 2,如果没有就等待。
// 3,如果有就开吃
// 4,吃完之后,桌子上的汉堡包就没有了
// 叫醒等待的生产者继续生产
// 汉堡包的总数量减一
//套路:
//1. while(true)死循环
//2. synchronized 锁,锁对象要唯一
//3. 判断,共享数据是否结束. 结束
//4. 判断,共享数据是否结束. 没有结束
while (true) {
try {
String take = bd.take();
System.out.println("吃货将" + take + "拿出来吃了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
using System;using System.Collections.Generic;using System.Linq;using System.Web;using AutoMapper;namespace MvcAutoMapper.AutoMapper{ public class Configuration { pu..._automapper有参profile
there is a string,for example。EXAMPLE。P></我怎么能中的remove the character,即从M,恩?我不need the队列。我想知道:P></Python中的字符串给端在任何特殊字符?which is a better left to right就是这样- shifting starting from the creation of charac...
Hello,While shutting down the database for offline backup, we get the following message in Alert log:Active call for process 5632 user 'SYSTEM' program 'ORACLE.EXE (SHAD)'SHUTDOWN: waiting for active ..._oracle shadow
一:介绍Prometheus官网是这么介绍,一个最初在SoundCloud上构建的开源系统监视和警报工具包。Prometheus是一个完整的监控和趋势系统,包括基于时间序列数据的内置和主动抓取,存储,查询,绘图和警报。简单来说,我们可以用Prometheus来监控包括linux,window系统,nginx以及mysql的性能指标。Grafana,一个将数据可视化的软件,具有非常高大上的ui设计。所以采用Grafana作为Prometheus的可视化工具二:安装1 安装window采集._windows11 安装 prometheus
经过连续几天的编制,安卓手机代码终于完成了,目前已经将我宿舍,家里,集控室的红外遥控电气设备完好的遥控了,另外还遥控了我的D7000相机,不错终于完工了。代码分为二类:各种电视、相机、等等遥控编码最简单,只要将按键的编码复制下来,直接变成手机发射码就可以了。(这种编码单个按键只发射单个信息)最难的是空调编码,由于空调编码是将单个按键要发射所有控制信息,如增加一度温度,同时要将控制模式、温度、风量..._安卓智能遥控器源代码哪里有
该楼层疑似违规已被系统折叠隐藏此楼查看此楼下面的代码是我刚才无聊写的。对于简单的一元多次方程的迭代#include #include #include #define MAXTIMES 5typedef int times;typedef double coefficient;typedef struct _properties{coefficient x; //系数times n;//次数}..._编写程序求解一元二次方程,根据判别式进行不同的处理
更改日志级别 在运行时中更改日志记录级别非常重要,这主要在生产环境中非常重要,在生产环境中,您可能希望在有限的时间内进行调试日志记录。 好了,更改根记录器非常简单–假设您有一个具有所需记录级别的输入参数,只需获取根记录器并通过输入记录级别进行设置,例如: Logger root = Logger.getRootLogger();//setting the logging leve..._how to change logging level in solon at runtime
前言过年放假前最后一天班,就想着做个简单又有趣的小东西。于是决定来写个自定义的LoadingView作为这个App框架的加载效果吧。走过路过点歌Start O(∩_∩)OGithub项目地址这篇文章叫你如何搭建手写LoadingView,看完这篇文章你能学会:属性动画使用自定义View--------------------------------关门,上分割线---------..._安卓仿饿了吗项目教程
1 下载安装包wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb遇到unable to resolve host address ‘http’的问题:Resolving http (http)... failed: Name or service not known.wget: unable to resolve host address ‘http’wget下载失败直接浏览器访问http_ubuntu18.0.4安装谷歌浏览器
GNU CC(简称为Gcc)是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C++和Object C等语言编写的程序。Gcc不仅功能强大,而且可以编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3和Ada等多种语言,而且Gcc又是一个交叉平台编译器,它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件,因此尤其适合在嵌入式领域的开发编译
使用python代码创建一个基于tcp协议的客户端:步骤:1、创建一个基于tcp协议的socket连接参数SOCK_STREAM:表示连接是一个tcp连接"2、和服务器建立连接调用connect方法连接到服务器"3、发送数据:upd使用sendto发送数据tcp使用send发送数据"4、关闭socketex:s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)s.connect('127.0.0.1', 9090)s.sen_文件传输支持tcp
腾讯地图产业版WeMap重磅升级。全新游戏引擎渲染,自动化三维构建,助力产业客户实现高逼真、低成本、快更新的三维地图应用。_腾讯地图产业版wemap