当前位置: 首页 > news >正文

阻塞队列的实现(线程案例)

一.什么是阻塞队列?

1.如果对于一个满的队列,还要把元素入队列,此时这个队列就会阻塞等待,一直阻塞到这个队列不满为止,从而把这个元素入队列

2.如果对于一个空的队列,还要从队列拿出元素,此时这个队列就会阻塞等待,一直阻塞到这个队列不空为止,从而把这个元素拿出队列

二.阻塞队列有什么作用:

1.解耦合作用

什么是耦合度:

耦合度高:就是模块之间联系比较紧密,彼此之间互相影响

耦合度低:就模块之间联系没有那么紧密,也就是模块之间影响小

此时引入阻塞队列,就可以有效降低模块之间的耦合度,起到了解耦合的作用!!

看图👇


2.削峰填谷作用


三.代码的实现

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ThreadDemo11 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
        queue.put("666");

        String s=queue.take();
        System.out.println(s);
        s=queue.take();
        System.out.println(s);
    }
}

此时阻塞等待👇

四.自己模拟实现

//实现阻塞队列
class MyBlockingQueue{

    //这个是模拟实现队列
    private String[] elems = null;

    private int head = 0;
    private int tail = 0;

    private int size = 0;

    private Object locker = new Object();


    //实现构造方法👇
    public MyBlockingQueue(int capacity){
        elems = new String[capacity];
    }


    //放进元素
    public void put (String elem) throws InterruptedException {
        synchronized (locker){
            //超出队列长度!
            //使用while循环,是防止线程唤醒后只判断一次,让被唤醒的线程再判断一次
            while(size >= elems.length){
                locker.wait();
            }
            //往这里加元素!!!
            elems[tail] = elem;
            tail++;
            //这里面是循环队列,可使这块空间能够被循环利用!,所以需要tail = 0;
            if(tail == elems.length){
                tail = 0;
            }
            size++;
            locker.notify();
        }

    }


    //拿出元素
    public String take() throws InterruptedException {
        String elem = null;
        //队列里面没有元素,阻塞等待
        synchronized (locker){
            while(size == 0){
                locker.wait();
            }

            //拿出一个元素
            elem = elems[head];
            head++;

            if(head == elems.length){
                head = 0;
            }
            size--;
            locker.notify();
        }
        return elem;
    }

}

public class ThreadDemo10 {

    public static void main(String[] args) {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(1000);
        //创建两个线程实现生产者和消费者模型
        Thread t1 =new Thread(()->{
            int n = 1;
            while(true){
                try {
                    myBlockingQueue.put(n+"");
                    System.out.println("生产元素"+n);
                    n++;

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });


        Thread t2 = new Thread(()->{
            while(true){
                try {
                    String n = myBlockingQueue.take();
                    System.out.println("消费元素"+n);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        //线程的启动!!
        t1.start();
        t2.start();
    }
}

代码中的一些问题

是否能够把里面这个while改成if👇

   if(size >= elems.length){
       locker.wait();
   }
答案是不能的,为什么呢

假设这个队列的容量是1,且此时容量已满。假设有3个线程t1,t2、t3.

t1和t2都调用put(),此时t1线程进入wait()状态,由于wait()状态,t1线程释放了锁。然后

t2也进入了锁里面,进入wait()状态,也释放了锁。

t3线程执行take(),从线程中拿出了一个元素,然后进行notify(),随机唤醒了其中一个线程,假设唤醒了t1线程,t1线程把一个元素放进了队列,此时队列满了,然后notify(),唤醒了t2

线程,由于if操作已经执行过了,所以它要把元素放进队列,由于队列容量为1,所以t2无法把元素放进队列中,此时代码便出现了问题。

由此可见,使用while()可以使线程唤醒之后,再判断一次条件,进而避免了线程安全的问题!


五.生产者-消费者模型

这就可以使用阻塞队列来完成

此时这个生产元素的速度要比消费元素速度要快,所以队列会先满了,然后阻塞等待,最后就是消费一个元素,生产一个元素,趋于平衡。

那么就会有人说,队列的容量不是1000,为什么会出现1001

原因就在于:线程的调度是不确定的。

当生产元素1000已经达到了队列的容量,这是毋庸置疑的。然后线程进行消费元素,然后notify(),唤醒了生产元素的线程,此时这个消费元素还没有打印,然后这个生产元素的线程打印了生产元素1001。

相关文章:

  • 计算机网络基础:认识网络拓扑结构
  • 生态安全相关文献推荐
  • Gravitino SparkConnector 实现原理
  • 线程POSIX信号量/基于环形队列的⽣产消费模型
  • 基础算法——高精度
  • 大模型小白入门
  • 深入浅出零拷贝技术:高性能IO的底层原理与Java/Linux实战
  • HMC7043和HMC7044芯片配置使用
  • AI 代理 x Sui:开启 Web3 自动化新时代!
  • 自动扶梯人员摔倒掉落识别检测数据集VOC+YOLO格式5375张2类别
  • 概率论基础概念
  • 【leetcode hot 100 238】除自身以外数组的乘积
  • 腾讯 TDF 即将开源 Kuikly 跨端框架,Kotlin 支持全平台
  • 自动化设备车间数据采集创新解决方案
  • 【pta】1031 查验身份证
  • 使用并行计算优化对拍
  • mmseg的decode_heads解析:理解语义分割解码器设计
  • ubuntu22.04下Meshlab打开obj文件闪退——使用Appimage并放入收藏夹中
  • LLM参数高效微调技术 PRFT
  • Qt QOCI driver available but not loaded(可用但未加载)
  • 许昌做网站公司/营销技巧五步推销法
  • 政府网站建设原则/搜索量最大的关键词
  • 网站高端设计/河南郑州网站顾问
  • 网站建设发展指引/天津百度网络推广
  • 公司企业网站建设注意事项/网站seo完整seo优化方案
  • php做网站开发有什么框架/域名138查询网