关于阻塞队列ArrayBlockingQueue和LinkedBlockingQueue

这两个阻塞队列前者是基于数组实现,后者是基于链表实现。

1.队列中的锁的实现不同

       ArrayBlockingQueue中的锁是没有分离的,即生产和消费用的是同一个锁;

       LinkedBlockingQueue中的锁是分离的,即生产用的是putLock,消费是takeLock

2.在生产或消费时操作不同

     ArrayBlockingQueue基于数组,在生产和消费的时候,是直接将枚举对象插入或移除的,不会产生或销毁任何额外的对象实例;

     LinkedBlockingQueue基于链表,在生产和消费的时候,需要把枚举对象转换为Node<E>进行插入或移除,会生成一个额外的Node对象,这在长时间内需要高效并发地处理大批量数据的系统中,其对于GC的影响还是存在一定的区别。

3.队列大小初始化方式不同

     ArrayBlockingQueue是有界的,必须指定队列的大小;

扫描二维码关注公众号,回复: 1870855 查看本文章

     LinkedBlockingQueue是无界的,可以不指定队列的大小,但是默认是Integer.MAX_VALUE。当然也可以指定队列大小,从而成为有界的。


看下ArrayBlockingQueue构造方法

   public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);//这里指定锁,并指定公平锁还是非公平锁
        notEmpty = lock.newCondition();//通过锁创建两个等待条件。生产线程和消费线程使用的是同一个锁
        notFull =  lock.newCondition();
    }

看看成员变量

 /** The queued items */
    final Object[] items;

    /** items index for next take, poll, peek or remove */
    int takeIndex;

    /** items index for next put, offer, or add */
    int putIndex;

    /** Number of elements in the queue */
    int count;//这里是int,注意 和LinkedBlockingQueue是不一样的

    /*
     * Concurrency control uses the classic two-condition algorithm
     * found in any textbook.
     */

    /** Main lock guarding all access */
    final ReentrantLock lock;
    /** Condition for waiting takes */
    private final Condition notEmpty;
    /** Condition for waiting puts */
    private final Condition notFull;

看看LinkedBlockingQueue的构造方法,和上面明显有区别,锁去哪了???

    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        last = head = new Node<E>(null);
    }

看看成员变量

/** Current number of elements */
    private final AtomicInteger count = new AtomicInteger(0);//因为锁分离,需要保证读写的对count的同步

    /**
     * Head of linked list.
     * Invariant: head.item == null
     */
    private transient Node<E> head;

    /**
     * Tail of linked list.
     * Invariant: last.next == null
     */
    private transient Node<E> last;

    /** Lock held by take, poll, etc */
    private final ReentrantLock 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();

按照实现原理来分析, ArrayBlockingQueue 完全可以采用分离锁,从而实现生产者和消费者操作的完全并行运行。 Doug Lea 之所以没这样去做,也许是因为 ArrayBlockingQueue 的数据写入和获取操作已经足够轻巧,以至于引入独立的锁机制,除了给代码带来额外的复杂性外,其在性能上完全占不到任何便宜。


ArrayBlockingQueue take操作并没有元素的移动 
采用的是 Circularly increment 

ArrayBlockingQueue的速度是优于LinkedBlocingQeque 
他们主要的区别是在bound上


猜你喜欢

转载自blog.csdn.net/u010365819/article/details/80841612