并发容器-阻塞队列

1.生产者-消费者模式使用阻塞队列

    1. 当队列满的时候,插入元素的线程被阻塞,直达队列不满。
    2. 队列为空的时候,获取元素的线程被阻塞,直到队列不空。
  • 生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这种生产消费能力不均衡的问题,便有了生产者和消费者模式。生产者和消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通信,而是通过阻塞队列来进行通信,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

2.常用方法

并发容器-阻塞队列_第1张图片
  • 抛出异常:当队列满时,如果再往队列里插入元素,会抛出IllegalStateException("Queuefull")异常。当队列空时,从队列里获取元素会抛出NoSuchElementException异常。
  • 返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移除方法,则是从队列里取出一个元素,如果没有则返回null。
  • 一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列空时,如果消费者线程从队列里take元素,队列会阻塞住消费者线程,直到队列不为空。
  • 超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出。

3.常用阻塞队列

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
    按照先进先出原则,要求设定初始大小

  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
    按照先进先出原则,可以不设定初始大小,默认Integer.Max_Value

  • ArrayBlockingQueue和LinkedBlockingQueue不同:
    1)锁上面:ArrayBlockingQueue只有一个锁,LinkedBlockingQueue用了两个锁,
    2)实现上:ArrayBlockingQueue直接插入元素,LinkedBlockingQueue需要转换。

  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
    默认情况下,按照自然顺序;如果需要定义自己的顺序,要么实现compareTo()方法,要么指定构造参数Comparator

  • DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    支持延时获取的元素的阻塞队列,元素必须要实现Delayed接口。
    适用场景:实现自己的缓存系统,订单到期,限时支付等等。
    参考超时订单

  • SynchronousQueue:一个不存储元素的阻塞队列。
    每一个put操作都要等待一个take操作

  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    transfer(),必须要消费者消费了以后方法才会返回,tryTransfer()无论消费者是否接收,方法都立即返回。

  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
    可以从队列的头和尾都可以插入和移除元素,实现工作密取,方法名带了First对头部操作,带了last从尾部操作,另外:add=addLast; remove=removeFirst; take=takeFirst

4. 阻塞队列的实现原理

参考生产消费问题与虚假唤醒(spurious wakeup)
比如,ArrayBlockingQueue就是基于Lock和Condition实现的。

    public void put(E e) throws InterruptedException {
        Objects.requireNonNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

参考

  • 1)享学课堂Mark老师笔记

你可能感兴趣的:(并发容器-阻塞队列)