Java并发编程知识点总结(十五)——BlockingQueue的各种实现

(一)、生产者-消费者问题

队列通常被视作线程间操作数据的容器,生产者将“生产”出来的数据放置在数据容器中,消费者只需要在“数据容器”中获取数据即可。阻塞队列(BlockingQueue)被广泛使用在“生产者-消费者”问题中,其原因是BlockingQueue提供了可阻塞的插入和可阻塞的移除方法。当队列容器满了,插入线程会被阻塞,直到队列容器空出位置。当队列容器空了,移除线程会被阻塞,直到队列容器不为空时。

(二)、BlockingQueue接口

public interface BlockingQueue<E> extends Queue<E> {
 
    boolean add(E e);

   
    boolean offer(E e);

 
    void put(E e) throws InterruptedException;


    boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;


    E take() throws InterruptedException;


    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;
    int remainingCapacity();


    boolean remove(Object o);


    public boolean contains(Object o);

 
    int drainTo(Collection<? super E> c);

  
    int drainTo(Collection<? super E> c, int maxElements);
}

因为BlockQueue接口继承自Queue接口,所以也有Queue接口中的方法,总结如下:
Queue

2.1 Queue接口中的方法
2.1.1 插入元素
方法功能
add(E e)往队列里插入元素,插入元素时会抛出illegalStateException
offer(E e)往队列里插入元素,插入成功返回true,否则返回false。当队列满时不抛出异常
2.1.2 删除元素
方法功能
remove(Object e)从队列中删除数据,成功则返回true,否则返回false
poll删除数据,当队列为空时,返回null
2.1.3 查看元素
方法功能
element获取队头元素,如果队列为空时则抛出NoSuchElementException异常
peek获取队头元素,如果为空,返回null,不会抛出异常
2.2 BlockingQueue接口中的方法
2.2.1 插入元素
方法功能
put()当阻塞队列容量已经满时,往阻塞队列插入数据的线程会被阻塞,直到阻塞队列有空位
offer(E e,long timeout,TimeUnit unit)当阻塞队列已经满时,同样会阻塞插入数据的线程,直到阻塞队列有空位。但是它可以限定超时时间,如果超过时间就直接返回
2.2.2 删除数据
方法功能
take()当阻塞队列为空时,获取队头数据的线程会被阻塞
poll(long timeout,TimeUnit unit)当阻塞队列为空时,获取数据的线程会被阻塞。如果超过给定的时间,会直接返回

(三)、BlockingQueue具体实现

3.1 ArrayBlockingQueue

ArrayBlockingQueue是由数组实现的有界阻塞队列,有FIFO的特点。ArrayBlockingQueue可以作为“有界数据的缓冲区”,生产者插入数据到阻塞队列中,消费者进行获取。ArrayBlockingQueue一旦创建,不能再被修改。
ArrayBlockingQueue默认情况下是不保证队列的公平性的,也就是说等待时间最久的消费者不一定是优先获取“产品”的。有可能存在等待时间最久的消费者一直无法获得资源,造成饥饿。但是如果保证公平性,会降低BlockQueue的吞吐量。可以通过如下代码来实现公平性:

BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(10,true);
3.2 LinkedBlockingQueue

LinkedBlockingQueue和ArrayBlockingQueue差不多,只不过是使用链表实现的,同样具有FIFO的特点,相比起来拥有更好的吞吐量。为了防止LinkedBlockingQueue容量增加过快,通常会指定长度。如果不指定长度,通常会使用Integer.MAX_VALUE。

3.3 PriorityBlockingQueue

PriorityBlockingQueue是一个支持优先级排序的无界阻塞队列,满足FIFO的特性,如果不指定就采用默认顺序进行排序,也可以通过自定义类实现compareTo()方法来指定元素排序规则,或者初始化时通过构造器Comparator来指定排序规则。

3.4 SynchronousQueue

SynchronousQueu实际上是不存储实际元素的,只有当有线程在删除数据时,其他线程才能插入数据。同样地,只有当有线程在插入数据时,其他线程才能删除数据。

3.5 LinkedTransferQueue

LinkedTransferQueue是一个由链表数据结构构成的无界阻塞队列,由于该队列实现了TransferQueue接口,与其他阻塞队列相比主要有以下不同的方法:

transfer(E e)
如果当前有线程(消费者)正在调用take()方法或者可延时的poll()方法进行消费数据时,生产者线程可以调用transfer方法将数据传递给消费者线程。如果当前没有消费者线程的话,生产者线程就会将数据插入到队尾,直到有消费者能够进行消费才能退出;

tryTransfer(E e)
tryTransfer方法如果当前有消费者线程(调用take方法或者具有超时特性的poll方法)正在消费数据的话,该方法可以将数据立即传送给消费者线程,如果当前没有消费者线程消费数据的话,就立即返回false。因此,与transfer方法相比,transfer方法是必须等到有消费者线程消费数据时,生产者线程才能够返回。而tryTransfer方法能够立即返回结果退出。

tryTransfer(E e,long timeout,imeUnit unit)
与transfer基本功能一样,只是增加了超时特性,如果数据才规定的超时时间内没有消费者进行消费的话,就返回false。

3.6 LinkedBlockingDeque

LinkedBlockingDeque是基于链表构成的有界阻塞双端队列,如果创建时不指定大小,默认值为Integer.Max_Value。这个阻塞队列不同点在于,是一个双端队列
在这里插入图片描述
下面是LinkedBlockingQueue和LinkedBlockingDeque的对比:
在这里插入图片描述

3.7 DelayDeque

DelayDeque是实现了Delay接口的无界阻塞队列,只有当数据对象达到延迟时间了才能插入到队列中进行存储。如果当前所有的数据都还没有达到创建时所指定的延时期,则队列没有队头,并且线程通过poll等方法获取数据元素则返回null。所谓数据延时期满时,则是通过Delayed接口的getDelay(TimeUnit.NANOSECONDS)来进行判定,如果该方法返回的是小于等于0则说明该数据元素的延时期已满。

参考文章:并发容器之BlockingQueue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值