- JDK7提供了6个阻塞队列。分别是ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue、SynchronousQueue、LinkedTransferQueue,
ArrayBlockingQueue
- ArrayBlockingQueue 是一个基于数组实现了的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。队列大小固定,之后不允许在增加数组容量。
- 重载的构造函数允许指定公平策略来排序等待的线程。如果为真,所有阻塞的插入和删除操作都按照FIFO顺序处理,默认为假,这样可能导致等待线程顺序不公平,而且可能导致部分饿死(就是一直获取不到资源)的问题,但同时也带来了提高吞吐量的好处。
LinkedBlockingQueue
- LinkedBlocklingQueue是一种无界的阻塞队列。
- 所谓阻塞队列,就是在入队时如果队列已满,线程会被阻塞,直到队列有空间供入队再返回;
- 同时在出队时,如果队列已空,线程也会被阻塞,直到队列中有元素供出队时再返回。
- LinkedBlocklingQueue基于链表实现,其出队和入队操作都会使用ReentrantLock进行加锁。所以本身是线程安全的,但同样的,只能保证入队和出队操作的原子性和一致性,在遍历时只能保证数据的弱一致性。
PriorityBlockingQueue
PriorityBlockingQueue一个无界阻塞队列,根据自然顺序决定队列中的元素优先级。它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞获取操作。虽然此队列逻辑上是无界的,但是资源被耗尽时试图执行 add 操作也将失败(导致 OutOfMemoryError)。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象(这样做会导致抛出 ClassCastException)。
DelayQueue
DelayQueue是一种特殊的优先级队列,按照每个元素的延迟时间(也就是在元素可以从队列中移除的剩余时间)进行排序。可想而知,队头肯定是离到期时间最短的元素了。如果都没到期,那就没有队头,poll是会返回null的。当然peek()查询队头元素和size()查询元素总数还是可以的。
SynchronousQueue
SynchronousQueue算是JDK实现的队列中比较奇葩的一个,它不能保存任何元素,size永远是0,peek()永远返回null。向其中插入元素的线程会阻塞,直到有另一个线程将这个元素取走,反之从其中取元素的线程也会阻塞,直到有另一个线程插入元素。
这种实现机制非常适合传递性的场景。也就是说如果生产者线程需要及时确认到自己生产的任务已经被消费者线程取走后才能执行后续逻辑的场景下,适合使用SynchronousQueue。
LinkedTransferQueue
- LinkedTransferQueue是一个由链表结构组成的无界阻塞TransferQueue队列。相对于其他阻塞队列,LinkedTransferQueue多了tryTransfer和transfer方法。
- LinkedTransferQueue采用一种预占模式。意思就是消费者线程取元素时,如果队列不为空,则直接取走数据,若队列为空,那就生成一个节点(节点元素为null)入队,然后消费者线程被等待在这个节点上,后面生产者线程入队时发现有一个元素为null的节点,生产者线程就不入队了,直接就将元素填充到该节点,并唤醒该节点等待的线程,被唤醒的消费者线程取走元素,从调用的方法返回。我们称这种节点操作为“匹配”方式。
非阻塞队列
PriorityQueue
- PriorityQueue是非阻塞队列,也不是线程安全的:
- 这种Queue并不是FIFO队列,而是根据元素的优先级进行排序,保证最小的元素最先出队,也可以在构造队列时传入Comparator实例,这样PriorityQueue就会按照Comparator实例的要求对元素进行排序。
ConcurrentLinkedQueue
ConcurrentLinkedQueue基于CAS的无锁技术,不需要在每个操作时使用锁,所以扩展性表现要更加优异,在常见的多线程访问场景,一般可以提供较高吞吐量。
网友评论