【总结】阻塞队列
该文章为面试精华版,如果是初学者,建议学习专栏:Java并发专栏
文章目录
一、ArrayBlockingQueue
-
用数组实现的有界阻塞队列。
-
此队列按照先进先出(FIFO)的原则对元素进行排序。
-
默认情况下不保证访问者公平的访问队列,所谓公平访问队列是指先阻塞的线程先访问,通常情况下为了保证公平性会降低吞吐量。
二、LinkedBlockingQueue
-
基于链表的无界阻塞队列,默认创建一个类似无限大小的容量
-
同 ArrayListBlockingQueue 类似,此队列按照先进先出(FIFO)的原则对元素进行排序。而
-
LinkedBlockingQueue 之所以能够高效的处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步,这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据,以此来提高整个队列的并发性能。
三、PriorityBlockingQueue
-
是一个支持优先级的无界队列。
-
默认情况下元素采取自然顺序升序排列。 可以自定义实现compareTo()方法来指定元素进行排序规则,或者初始化 PriorityBlockingQueue 时,指定构造参数 Comparator 来对元素进行排序。需要注意的是不能保证同优先级元素的顺序。
四、DelayQueue
-
是一个支持延时获取元素的无界阻塞队列。
-
队列使用 PriorityQueue 来实现。队列中的元素必须实现 Delayed 接口,在创建元素时可以指定多久才能从队列中获取当前元素。只有在延迟期满时才能从队列中提取元素。
-
可以用于:缓存系统设计,当可以取出任务时,代表缓存期到了;定时任务调度 ,保 存 当 天 将 会 执 行 的任务和执行时间 ,一旦从DelayQueue 中获取到任务就开始执行
五、SynchronousQueue
- 是一个不存储元素的阻塞队列。
- 每一个put 操作必须等待一个 take 操作,否则不能继续添加元素。
- 负责把生产者线程处理的数据直接传递给消费者线程。队列本身并不存储任何元素,非常适合于传递性场景,比如在一个线程中使用的数据,传递给另外一个线程使用
- SynchronousQueue 的 吞 吐 量 高 于 LinkedBlockingQueue 和ArrayBlockingQueue
六、LinkedTransferQueue
是 一 个 由 链 表 结 构 组 成 的无界阻塞 TransferQueue 队 列 。 相对于其他阻塞队列 ,LinkedTransferQueue 多了 tryTransfer 和 transfer 方法,是一种预占的模式,判断有没有线程空闲,有就直接让其拿走,没有就占着这个位置直到任务被拿到或者超时或者中断:
- transfer 方法: 如果当前有消费者正在等待接收元素(消费者使用 take()方法或带时间限制的poll()方法时), transfer 方法可以把生产者传入的元素立刻 transfer(传输)给消费者。如果没有消费者在等待接收元素, transfer 方***将元素存放在队列的 tail 节点,并等到该元素被消费者消费了才返回。
- tryTransfer 方法。则是用来试探下生产者传入的元素是否能直接传给消费者。如果没有消费者等待接收元素,则返回 false。和 transfer 方法的区别是 tryTransfer 方法无论消费者是否接收,方法立即返回。而 transfer 方法是必须等到消费者消费了才返回。
对于带有时间限制的 tryTransfer(E e, long timeout, TimeUnit unit)方法,则是试图把生产者传入的元素直接传给消费者,但是如果没有消费者消费该元素则等待指定的时间再返回,如果超时还没消费元素,则返回 false,如果在超时时间内消费了元素,则返回 true。
七、LinkedBlockingDeque
是一个由链表结构组成的双向阻塞队列。所谓双向队列指的你可以从队列的两端插入和移出元素。双端队列因为多了一个操作队列的入口,在多线程同时入队时,也就减少了一半的竞争。
在初始化 LinkedBlockingDeque 时可以设置容量防止其过渡膨胀。另外双向阻塞队列可以运用在
“工作窃取”模式中,Fork/Join框架会窃取其他线程队尾的任务。