阻塞队列(BlockingQueue)实现生产消费模式并手写一个阻塞队列

阻塞队列(BlockingQueue)什么是阻塞队列?阻塞队列 是一种数据结构,它是一个队列,可以存放0到N个元素。我们可以对这个队列执行插入或弹出元素操作,弹出元素操作就是获取队列中的第一个元素,并且将其从队列中移除;而插入操作就是将元素添加到队列的末尾。当队列中没有元素时,对这个队列的弹出操作将会被阻塞,直到有元素被插入时才会被唤醒;当队列已满时,对这个队列的插入操作就会被阻塞,直到有元素被弹出后才会被唤醒。在线程池中,往往就会用阻塞队列来保存那些暂时没有空闲线程可以直接执行的任务,等到线程空闲之
摘要由CSDN通过智能技术生成

阻塞队列(BlockingQueue)

什么是阻塞队列?

阻塞队列 是一种数据结构,它是一个队列,可以存放0到N个元素。我们可以对这个队列执行插入或弹出元素操作,弹出元素操作就是获取队列中的第一个元素,并且将其从队列中移除;而插入操作就是将元素添加到队列的末尾。当队列中没有元素时,对这个队列的弹出操作将会被阻塞,直到有元素被插入时才会被唤醒;当队列已满时,对这个队列的插入操作就会被阻塞,直到有元素被弹出后才会被唤醒。

在线程池中,往往就会用阻塞队列来保存那些暂时没有空闲线程可以直接执行的任务,等到线程空闲之后再从阻塞队列中弹出任务来执行。一旦队列为空,那么线程就会被阻塞,直到有新任务被插入为止。

在多线程中,阻塞的意思是,在某些情况下会挂起线程,一旦条件成熟,被阻塞的线程就会被自动唤醒。

阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

在阻塞队列不可用时,这两个附加操作提供了4种处理方式

img

**抛出异常:**是指当阻塞队列满时候,再往队列里插入元素,会抛出 IllegalStateException(“Queue full”) 异常。当队列为空时,从队列里获取元素时会抛出 NoSuchElementException 异常 。

**返回特殊值:**插入方法会返回是否成功,成功则返回 true。移除方法,则是从队列里拿出一个元素,如果没有则返回 null

**一直阻塞:**当阻塞队列满时,如果生产者线程往队列里 put 元素,队列会一直阻塞生产者线程,直到拿到数据,或者响应中断退出。当队列空时,消费者线程试图从队列里 take 元素,队列也会阻塞消费者线程,直到队列可用。

**超时退出:**当阻塞队列满时,队列会阻塞生产者线程一段时间,如果超过一定的时间,生产者线程就会退出。

如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put或offer方法永远不会被阻塞,而且使用offer方法时,该方法永远返回true

JDK7 提供了 7 个阻塞队列

ArrayBlockingQueue一个由数组结构组成的有界阻塞队列。

LinkedBlockingQueue一个由链表结构组成的有界阻塞队列(默认值是Integer.MAX_VALUE)。

PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

DelayQueue:一个使用优先级队列实现的无界阻塞队列。

SynchronousQueue一个不存储元素的阻塞队列 , 单个元素的队列。

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

ArrayBlockingQueue 与LinkedBlockingQueue

ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);

LinkedBlockingQueuefairQueue = new LinkedBlockingQueue(1000,true);

ArrayBlockingQueue 和LinkedBlockingQueue区别:

  1. 底层实现不同

    ArrayBlockingQueue 底层使用数组来维护队列,

    LinkedBlockingQueue 底层使用链表来维护队列,在添加和删除队列中的元素的时候,会创建和销毁节点对象,在高并发和大量数据的时候,GC压力很大。

  2. 锁的方式不同

    ArrayBlockingQueue 获取数据和添加数据都是使用同一个锁对象,这样添加和获取就不是一个并发的过程,不过,在ArrayBlockingQueue 中使用Condition的等待/通知机制,这样使得ArrayBlockingQueue的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜

LinkedBlockingQueue 获取数据和添加数据使用不同的锁对象。

SynchronousQueue

SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素。它支持公平访问队列。默认情况下线程采用非公平性策略访问队列。使用以下构造方法可以创建公平性访问的SynchronousQueue,如果设置为true,则等待的线程会采用先进先出的顺序访问队列

SynchronousQueue<E> queue = new SynchronousQueue<E>(true);

**SynchronousQueue可以看成是一个传球手,负责把生产者线程处理的数据直接传递给消费者线程。**队列本身并不存储任何元素,非常适合传递性场景。SynchronousQueue的吞吐量高于LinkedBlockingQueue和ArrayBlockingQueue。

为什么使用堵塞队列?

在多线程领域,所谓堵塞就是在某些情况下挂起线程(堵塞),一旦条件满足,被挂起的线程又会自动被唤醒。

为什么使用BlockingQueue

好处是我们不需要关系什么时候需要堵塞线程,什么时候需要唤醒线程,因为这一切都没BlockingQueue包办了,在JUC包发布之前,在多线程环境下,我们需要手动去控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

堵塞队列版生产消费模式:

public class ProdAndCons {
   
    public static void main(String[] args) {
   
        //设置长度
        MyResource myResource = new MyResource(new ArrayBlockingQueue<String>(10));
        new Thread(() ->{
   
            try {
   
           
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值