关于阻塞队列ArrayBlockingQueue和LinkedBlockingQueue

xiaoxiao2021-02-28  9

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

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

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

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

 

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

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

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

 

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

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

     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上

转载请注明原文地址: https://www.6miu.com/read-1900367.html

最新回复(0)