LinkedBlockingQueue应用详解

一. 概述及应用场景
Jdk源码注释:(JDK1.9)

/**
 1. An optionally-bounded {@linkplain BlockingQueue blocking queue} based on
 2. linked nodes.
 3. This queue orders elements FIFO (first-in-first-out).
 4. The <em>head</em> of the queue is that element that has been on the
 5. queue the longest time.
 6. The <em>tail</em> of the queue is that element that has been on the
 7. queue the shortest time. New elements
 8. are inserted at the tail of the queue, and the queue retrieval
 9. operations obtain elements at the head of the queue.
 10. Linked queues typically have higher throughput than array-based queues but
 11. less predictable performance in most concurrent applications.
 12.  13. <p>The optional capacity bound constructor argument serves as a
 14. way to prevent excessive queue expansion. The capacity, if unspecified,
 15. is equal to {@link Integer#MAX_VALUE}.  Linked nodes are
 16. dynamically created upon each insertion unless this would bring the
 17. queue above capacity.
 18.  19. <p>This class and its iterator implement all of the <em>optional</em>
 20. methods of the {@link Collection} and {@link Iterator} interfaces.
 21.  22. <p>This class is a member of the
 23. <a href="{@docRoot}/java/util/package-summary.html#CollectionsFramework">
 24. Java Collections Framework</a>.
 25.  26. @since 1.5
 27. @author Doug Lea
 28. @param <E> the type of elements held in this queue
 */

其意思是:是一个可变范围的链表,队列的元素是先进先出(FIFO),首元素在队列中待的时间最长,尾元素在队列中待的时间最短。新元素添加在队列的尾部,检索操作获得队列的头部。 有更大的吞吐量比起 ,但是更少的可预见性操作在
并发应用中。该队列的容量大小是可变的,如果在构造函数中没有指定,默认的容量是 ,链表节点会被动态的创建,除非此操作会导致超出容量。此类的它的迭代器实现了 和 接口的所有方法。
从源码中可以看到,该类继承了AbstractQueue,实现了BlockingQueue,
一般情况下,在处理多线程的并发问题时,常常用到此类。
二. 源码分析
1. 链表节点

static class Node<E> {...}

2.容量大小

private final int capacity;

如果没有指定,则为Integer.MAX_VALUE,支持原子操作
3.当前元素数量

private final AtomicInteget count = new AtomicInteget();

4.链表头节点,其前驱节点为null

transient Node<E> head;

5.链表尾节点,其后继为null

private transient Node<E> last;

6.针对取和添加操作的两把锁及其上的条件

/**Lock held by tack,poll,etc*/
private final ReentrantLook takeLock=new ReentrantLock();
/**Wait queue for waiting takes*/
private final Condition notEmpty=takeLock.newCondition();
/**Lock held by put,offer,etc*/
private final ReentrantLock putLock=new ReentrantLock();
/**Wait queue for waiting puts*/
private final Condition notFull=putLock.newCondition();

三.主要方法
1.LinkedBlockingQueue的put方法

/**添加一个元素到队尾,如果队列已满,则一直处于阻塞状态,直到有可用空间
*/
public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    int c = -1;
    Node<E> node = new Node<E>(e);
    final ReentrantLock putLock = this.putLock;
    final AtomicInteger count = this.count;
    //如果当前线程未被中断,则获取锁
    putLock.lockInterruptibly();
    try {
         /*当容量已满时,等待notfull条件释放锁,陷入等
         待状态有两种方法会激活该线程:
        1.  某个put方法添加元素后,发现队列有空余,就调用  notFull.signal()方法激活阻塞线程;
        2.  Take线程取元素时,发现队列已满,取出元素后,也会调用notFull.signal()方法激活阻塞线程*/
        while (count.get() == capacity) {
            notFull.await();
        }
        //把元素node添加到队尾
        enqueue(node);
        c = count.getAndIncrement();
        //发现队列未满,调用notFull.signal()激活阻塞线程的put线程
        if (c + 1 < capacity)
            notFull.signal();
    } finally {
        putLock.unlock();
    }
    if (c == 0)
        signalNotEmpty();
}
  1. LinkedBlockingQueue的take方法
public E take() throws InterruptedException {
    E x;
    int c = -1;
    final AtomicInteger count = this.count;
    final ReentrantLock takeLock = this.takeLock;
    takeLock.lockInterruptibly();
    try {
        //若容量为空,等待非空条件
        while (count.get() == 0) {
            notEmpty.await();
        }
        x = dequeue();
        c = count.getAndDecrement();
        //再次激活notEmpty
        if (c > 1)
            notEmpty.signal();
    } finally {
        takeLock.unlock();
    }
    //如果容量已满,调用signalNotFull(),put线程处于阻塞状态。
    if (c == capacity)
        signalNotFull();
    return x;
}

由以上可以看出其生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的操作下生产者和消费者可以并行的操作队列中的数据,依次来提高整个队列的并发性能。在这里,我们需要注意的是:如果构造一个LinkedBlockingQueue对象,而没有指定其大小,LinkedBlockingQueue会默认为Integer.MAX_VALUE,这样的话,如果生产者的速度大于消费者的速度,也许还没有等队列阻塞产生,系统内存就已经被消耗殆尽了。
四.应用举例
实现生产者消费者模型

package 阻塞队列;
import java.util.concurrent.LinkedBlockingQueue;
/**
 * LinkedBlockingQueue实现生产者消费者模型
 */
public class LinkedBlockingQueueTest {
    //LinkedBlockingQueue<String> queue=new LinkedBlockingQueue<String>(3);
    private int i;
    LinkedBlockingQueue<Integer> queue= new LinkedBlockingQueue<>(3);

    class Producer implements Runnable{
        @Override
        public void run() {
            while (true){
                try {
                    queue.put(i++);
                    //System.out.print("生产,,,,,,");
                    System.out.println("生产,,,,,,剩余容量:"+queue.remainingCapacity());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    class Consumer implements Runnable{
        @Override
        public void run() {
            while (true){
                try {
                    queue.take();
                    System.out.println("消费,,,,剩余容量:"+queue.remainingCapacity());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    public static void main(String[] args){
        LinkedBlockingQueueTest linkedBlockingQueueTest=new LinkedBlockingQueueTest();
        new Thread(linkedBlockingQueueTest.new Producer()).start();
        new Thread(linkedBlockingQueueTest.new Consumer()).start();
    }
}
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值