Java并发包concurrent——BlockingQueue

本文详细介绍了Java并发包中的BlockingQueue接口及其各种实现,包括ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue和DelayQueue的实现原理、特点及使用场景。 BlockingQueue是一种线程安全的数据结构,提供了在多线程环境下高效的数据共享机制。
摘要由CSDN通过智能技术生成

目录

1. BlockingQueue接口

2. BlockingQueue的分类

3. 有界阻塞队列——ArrayBlockingQueue和LinkedBlockingQueue

4. 优先无界阻塞队列——PriorityBlockingQueue

5. 同步阻塞队列——SynchronousQueue

6. 延时阻塞队列——DelayQueue


BlockingQueue是java.util.concurrent包中定义的一个阻塞队列的接口,concurrent包中实现了多种阻塞队列,它们都实现了该接口。有了这些阻塞队列,可以更好的实现多线程之间的数据共享;而如果能灵活运用这些类,便可以在工作中更好的实现高质量的多线程高并发程序。本章首先从BlockingQueue接口开始,详细分析了concurrent包中的这些阻塞队列的实现。

1. BlockingQueue接口

        BlockingQueue接口继承了Queue接口,所以其本身也可以作为一般的队列使用。在此基础上,BlockingQueue定义了如下几个阻塞方法:

  1. void put(E e) throws InterruptedException; 该方法为阻塞式入队方法,如果发生中断异常会抛出。
  2. boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; 该方法为阻塞式入队方法,该方法可以设置一个超时时间,当发生超时时可返回false表示入队失败。
  3. E take() throws InterruptedException; 该方法为阻塞式出队方法,如果发生中断异常会抛出。
  4. E poll(long timeout, TimeUnit unit) throws InterruptedException; 该方法为阻塞式出对方法,该方法也可以设置一个超时时间,如果发生超时,将会返回null。

2. BlockingQueue的分类

        concurrent包中定义了多种阻塞队列:

  1.  ArrayBlockingQueue:有界阻塞队列,通过数组实现,在创建对象时必须指定该队列的容量。
  2.  LinkedBlockingQueue:有界阻塞队列,通过链表实现,在创建对象时可以指定该队列的容量,如果不指定容量默认为Integer类型的最大值。
  3.  PriorityBlockingQueue:无界阻塞队列,可指定初始容量,可指定队列的优先级。
  4. SynchronousQueue:同步阻塞队列,该阻塞队列比较特殊,其不存储元素,如果该队列中有一个元素,则其他线程不能再往里面插入元素;只有等消费者线程消费之后才能继续插入元素,反之也是一样。
  5. DelayQueue:延时阻塞队列,该队列可设置延时,入队的元素只有在延时期满之后才能消费。
  6. LinkedTransferQueue:无界阻塞队列,通过链表实现,相比其他队列,该队列多了两个方法transfer()和tryTransfer()。
  7. LinkedBlockingDeque:双向阻塞队列,该队列也是通过链表实现,多线程并发时可将锁竞争缩减一半。

        本节将concurrent包中所有的阻塞队列的特点进行了简要的概括,下文将详细分析每种阻塞队列的实现原理与使用场景等。

3. 有界阻塞队列——ArrayBlockingQueue和LinkedBlockingQueue

        concurrent包实现了两种有界阻塞队列:ArrayBlockingQueue和LinkedBlockingQueue,顾名思义,ArrayBlockingQueue通过数组实现阻塞队列,LinkedBlockingQueue则通过链表实现阻塞队列。两者的实现方案类似,因此,在此以ArrayBlockingQueue为例,介绍有界阻塞队列的实现原理。

        在ArrayBlockingQueue中定义了两个方法用来为阻塞队列插入和删除元素。

private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    notEmpty.signal();
}

private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];
    items[takeIndex] = null;
    if (++takeIndex == items.length)
        takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();
    return x;
}

        其中,enqueue方法为队列添加元素,dequeue方法为队列删除元素。从这两个方法可以看出,ArrayBlockingQueue中定义了一个items数组用来保存阻塞队列的元素,然后分别定义了putIndex和takeIndex来表示插入和删除元素的位置。当putIndex或takeIndex增长为数组的长度时,会将其置零,表示该队列为一个循环队列。ArrayBlockingQueue中的元素个数则用count来统计。ArrayBlockingQueue可通过构造方法指定阻塞队列的容量capacity。

        ArrayBlockingQueue基于ReentrantLock和Condition来实现阻塞队列和线程安全。ArrayBlockingQueue定义了一个ReentrantLock对象lock和两个Condition对象notEmpty和notFull。首先,在调用阻塞队列相关的方法时,通过ReentrantLock为操作加锁;然后进行入队和出队操作。入队时,如果队列已满,对于阻塞类的入队方法会调用notFull的await等待,直到有线程调用出队方法,此时会触发notFull的signal方法通知阻塞的线程入队。反之,如果队列为空,对于阻塞类的出队方法会调用notEmpty的await方法等待,直到有线程调用入队方法,就会触发notEmpty的signal方法通知阻塞的线程出队。而对于非阻塞类的入队和出对方法,入队和出队则直接返回,不会阻塞。

        LinkedBlockingQueue是基于链表的有界阻塞队列,因为都是有界阻塞队列,所以其实现与ArrayBlockingQueue类似,在此不再赘述其实现原理。这里仅总结一下ArrayBlockingQueue和LinkedBlockingQueue的异同点,以便在平时应用中选择更加合适的队列。

        ArrayBlockingQueue和LinkedBlockingQueue都是有界阻塞队列,也就是说队列的容量有限制,都是通过构造函数来指定队列的容量。而其不同点主要体现在以下几个方面:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值