【多线程】阻塞队列 | put()方法 | take()方法 | 生产者-消费者模式 |实现阻塞队列

文章目录

    • 阻塞队列
      • 1.生产者-消费者模式
        • 生产者消费者模型的意义:
          • 1.解耦合
          • 2.削峰填谷:
      • 2.阻塞队列的使用
            • BlockingQueue
      • 3.实现阻塞队列
          • 唤醒:
          • 使用阻塞队列实现生产者消费者模型


阻塞队列

阻塞队列是一种特殊的队列:

  • 1.是线程安全的。

  • 2.带有阻塞特性

    如果队列为空,继续出队列,就会发生阻塞。直到其他线程往队列中添加队列为止

    如果队列为满,继续入队列, 也会发生阻塞,直到其他线程从队列中取走元素为止

阻塞队列可以来实现生产者-消费者模型。

1.生产者-消费者模式

生产者:把生产出来的内容,放到阻塞队列中。

消费者:从阻塞队列中获取内容。

生产者消费者模型的意义:
1.解耦合

两个模块联系越紧密,耦合就越高。对于分布式系统来说,更加有意义。

在这里插入图片描述

可以使用生产者-消费者模型,实现解耦合的效果。

在这里插入图片描述

2.削峰填谷:

峰:短时间内,请求量比较多时。

谷:请求量比较少时。

在这里插入图片描述

​ 在这种情况下:高峰时段,一旦客户端发起的请求量非常多时,每个A收到的请求,都会立即发给B。此时,A和B的访问量是相同的。但是在实际上,由于不同的服务器,上面跑的业务不同。虽然访问量一样,单个访问,消耗的硬件资源是不一样的。可能服务器A可以承担这些并发量,但是服务器B承担不了,就会挂掉。

在引入了生产者-消费者模型之后,就会解决这类问题。

在这里插入图片描述

  • 当服务器A收到了大量请求之后,A会把对应的请求写入到队列中。B仍然按照之前的节奏来处理请求。(削峰)
  • 一般情况下,峰值不会持续存在,峰值过后,A的请求量就会恢复正常、甚至减低。服务器B就可以在此时,逐渐把积压的请求给处理掉。(填谷)。

2.阻塞队列的使用

BlockingQueue

在这里插入图片描述

  • BlockingQueue是一个具体的接口,所以需要new一个具体的实现。

  • 同时BlockingQueue继承自Queue。也可以使用Queue的方法(没有阻塞属性)

    1.基于数组实现

    2.基于链表实现

  • BlockingQueue带有阻塞的方法:

    ​ put:阻塞式入队列

    ​ take:阻塞式出队列

    没有提供阻塞式获取队首元素的方法。

    public static void main(String[] args) {// BlockingQueue<String> queue = new ArrayBlockingQueue<>();BlockingQueue<String>queue = new LinkedBlockingQueue<>();queue.put("111");queue.put("222");queue.put("333");queue.put("444");String elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);}

3.实现阻塞队列

给一个普通的队列加上线性安全和阻塞

对入队和出队的方法进行加锁。对数据的修改实现原子性操作,保证线程安全

  • put入队的时候,如果队列满了,就进行阻塞(wait)

在出队的时候,当size–后,队列中有位置了,调用notify()方法,对阻塞的put方法进行唤醒。

  • 同样的,如果take出队列时,队列为空的话,也需要进行阻塞。

在入队时,当size++后,队列不为空了,调用notify()方法,对阻塞的take方法进行唤醒。

在这里插入图片描述

  • 一个队列的阻塞情况,要么为空、要么为满。

    put和take只有一边能阻塞。如果put阻塞了,其他线程继续调用put,也会进行阻塞。只有靠take来唤醒。

    take阻塞,其他线程继续调用take也会进行阻塞,只能靠put来唤醒。

唤醒:

wait方法除了使用notify()方法进行唤醒,还可以通过interrupt()方法,来中断wait的状态。

使用interrupt方法唤醒的时候,会出现InterruptedException异常

public void put(String elem) throws InterruptedException {}

因为是throws抛出的异常,执行到interrupt()方法后,整个方法就会结束。

    public void put(String elem) {synchronized (this) {if (size == data.length) {try {this.wait();}catch (InterruptedException e){ }}data[tail] = elem;tail++;if (tail == data.length) {tail = 0;}size++;this.notify();}
  • 如果是try-catch来处理异常。如果出现异常,程序仍会继续执行下去。在满队列的情况下。强行修改,会覆盖掉tail的值,并且size会超出数组长度。

​ 使用wait时,要考虑wait是notify唤醒的,还是通过Interrupt唤醒的。在wait返回时,还要进行判断wait执行的条件符不符合。可以直接将wait写在while循环中。循环的条件就是wait执行的条件。使wait在唤醒之后,再确定一下,条件是否满足。

            while (size == data.length) {//队列满了,就会进行堵塞this.wait();}
  • 最终再通过volatile修饰要频繁修改的变量,避免出现内存可见性问题。
class MyBlockingQueue {private String[] data = new String[1000];private  volatile int head = 0;//队列起始位置private volatile int tail = 0;//队列结束位置的下一个元素。private volatile int size = 0;//队列中有效元素个数//入队public void put(String elem) throws InterruptedException {synchronized (this) {while (size == data.length) {//队列满了,就会进行堵塞this.wait();}//队列没满,向队列添加元素data[tail] = elem;tail++;if (tail == data.length) {//满了之后,环形队列要回到开头。tail = 0;}size++;this.notify();//唤醒take中的wait}}//出队public String take() throws InterruptedException {synchronized (this) {while (size == 0) {//队列为空时this.wait();}//队列不空时,把队首head位置删除String ret = data[head];head++;if (head == data.length) {head = 0;}size--;this.notify();//唤醒put中的wait.return ret;}}
}
使用阻塞队列实现生产者消费者模型
    public static void main(String[] args) {MyBlockingQueue queue = new MyBlockingQueue();//消费者Thread t1 = new Thread(() -> {while (true){try {String res =  queue.take();System.out.println("消费元素: "+res);Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//生产者Thread t2 = new Thread(() -> {int num = 1;while (true){try {queue.put(num+" ");System.out.println("生产元素:"+num);num++;} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}生产元素:1001
生产元素:1002
消费元素: 2 
消费元素: 3 
生产元素:1003
消费元素: 4 
生产元素:1004
  • 生产者快速生产了1000多个,消费者才消耗几个。队列填满之后,生产者进入了阻塞。直到消费者消费了之后,才会进行生产。消费一个生产一个。

点击移步博客主页,欢迎光临~

偷cyk的图

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/624071.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux管理进程

一、程序 程序&#xff1a;执行特定任务的一串代码 1.是一组计算机能识别和执行的指令&#xff0c;运行于电子计算机上&#xff0c;满足人们某种需求的信息化工具 2.用于描述进程要完成的功能&#xff0c;是控制进程执行的指令集 二、进程和线程 1.进程 进程是程序的执行…

CSS导读 (CSS的背景 上)

&#xff08;大家好&#xff0c;今天我们将继续来学习CSS的相关知识&#xff0c;大家可以在评论区进行互动答疑哦~加油&#xff01;&#x1f495;&#xff09; 目录 四、CSS的背景 4.1 背景颜色 4.2 背景图片 4.3 背景平铺 4.4 背景图片位置 4.4.1 参数是方位名词 …

[蓝桥杯 2019 国 B] 解谜游戏

[蓝桥杯 2019 国 B] 解谜游戏 题目背景 题目描述 小明正在玩一款解谜游戏。谜题由 24 24 24 根塑料棒组成&#xff0c;其中黄色塑料棒 4 4 4 根&#xff0c;红色 8 8 8 根&#xff0c;绿色 12 12 12 根 (后面用 Y 表示黄色、R 表示红色、G 表示绿色)。初始时这些塑料棒排…

代码随想录刷题随记23-回溯3

代码随想录刷题随记23-回溯3 39. 组合总和 leetcode链接 注意同一个 数字可以 无限制重复被选取 怎么体现这个可以重复取的思想很重要 解题代码&#xff1a; class Solution { public:void backtrace( vector<vector<int>>& ret,vector<int> &pat…

鸿蒙端云一体化开发--调用云函数--适合小白体制

如何实现在端侧调用云函数&#xff1f; 观看前&#xff0c;友情提示&#xff1a; 不知道《如何一键创建端云一体化模板》的小白同学&#xff0c;请看&#xff1a; 鸿蒙端云一体化开发--开发云函数--适合小白体制-CSDN博客 实现方法&#xff1a; 第一步&#xff1a;添加依赖 …

android studio 网络请求okhttp3、okgo

一、在build.gradle文件里添加 implementation com.squareup.okhttp3:okhttp:4.9.0 implementation com.squareup.okhttp3:okhttp:3.12.0 implementation com.squareup.okio:okio:1.17.4 implementation com.lzy.net:okgo:3.0.4 implementation com.alibaba:fastjson:1.2.57 i…

Linux之 USB驱动框架-usb-skeleton.c usb驱动源码分析(3)

一、usb 驱动框架图 二、 usb 设备经典驱动&#xff1a;usb-skeleton.c 驱动 路径&#xff1a; drivers/usb/usb-skeleton.c USB骨架程序可以看做一个最简单的USB设备驱动的实例&#xff0c;其分析流程大致如下&#xff1a; static struct usb_driver skel_driver { …

hot100 -- 链表(中)

不要觉得力扣核心代码模式麻烦&#xff0c;它确实比不上ACM模式舒服&#xff0c;可以自己处理输入输出 只是你对 链表 和 return 的理解不到位 &#x1f442; ▶ 屿前世 (163.com) &#x1f442; ▶ see you tomorrow (163.com) 目录 &#x1f382;两数相加 &#x1f6a9;删…

使用阿里云试用Elasticsearch学习:5. 地理位置

我们拿着纸质地图漫步城市的日子一去不返了。得益于智能手机&#xff0c;我们现在总是可以知道 自己所处的准确位置&#xff0c;也预料到网站会使用这些信息。我想知道从当前位置步行 5 分钟内可到的那些餐馆&#xff0c;对伦敦更大范围内的其他餐馆并不感兴趣。 但地理位置功…

【CAN】采样点介绍及测试方法

文章目录 1 什么是采样点2 为什么需要采样点3 采样点的计算公式4 VH6501测试原理和方法4.1 VH6501测试采样点原理4.2 VH6501测试方法 >>返回总目录<< 1 什么是采样点 采样点是节点判断信号逻辑电平的位置&#xff0c;是CAN控制器读取总线电平&#xff0c;并解释各…

Big Data and Cognitive Computing (IF=3.7) 计算机/大数据/人工智能期刊投稿

Special Issue: Artificial Cognitive Systems for Computer Vision 欢迎计算机/大数据/人工智能/计算机视觉相关工作的投稿&#xff01; 影响因子3.7&#xff0c;截止时间2024年12月31日 投稿咨询&#xff1a;lqyan18fudan.edu.cn 投稿网址&#xff1a;https://www.mdpi.com/j…

【多模态检索】Coarse-to-Fine Visual Representation

快手文本视频多模态检索论文 论文&#xff1a;Towards Efficient and Effective Text-to-Video Retrieval with Coarse-to-Fine Visual Representation Learning 链接&#xff1a;https://arxiv.org/abs/2401.00701 摘要 近些年&#xff0c;基于CLIP的text-to-video检索方法…