【并发编程】如何选择适合的阻塞队列

常见阻塞队列

  • ArrayBlockingQueue:基于数组结构实现的一个有界阻塞队列。
  • LinkedBlockingQueue:基于链表结构实现的一个无界阻塞队列。
  • PriorityBlockingQueue:支持按优先级排序的无界阻塞队列。
  • DelayQueue:基于优先级队列(PriorityBlockingQueue)实现的无界阻塞队列。
  • SynchronousQueue:不存储元素的阻塞队列。
  • LinkedTransferQueue:基于链表结构实现的一个无界阻塞队列。
  • LinkedBlockingDeque:基于链表结构实现的一个双端阻塞队列。

线程池默认选择的阻塞队列

  • FixedThreadPool(SingleThreadExecutor 同理):选取的是 LinkedBlockingQueue。
  • CachedThreadPool:选取的是 SynchronousQueue。
  • ScheduledThreadPool(SingleThreadScheduledExecutor同理):选取的是延迟队列。

从功能角度考虑选择阻塞队列

  • 比如是否需要阻塞队列帮我们排序,如优先级排序、延迟执行等。如果有这个需要,我们就必须选择类似于 PriorityBlockingQueue之类的有排序能力的阻塞队列。

从容量角度考虑选择阻塞队列

  • 是否有存储的要求,还是只需要“直接传递”。
  • 容量固定的:ArrayBlockingQueue;
  • 容量无限的:LinkedBlockingQueue;
  • 没有任何容量的:SynchronousQueue;
  • 而对于 DelayQueue 而言,它的容量固定就是 Integer.MAX_VALUE。

从能否扩容角度考虑选择阻塞队列

  • 有时我们并不能在初始的时候很好的准确估计队列的大小,因为业务可能有高峰期、低谷期。如果一开始就固定一个容量,可能无法应对所有的情况,也是不合适的,有可能需要动态扩容。
  • 如果我们需要动态扩容的话,那么就不能选择 ArrayBlockingQueue ,因为它的容量在创建时就确定了,无法扩容。
  • 相反,PriorityBlockingQueue 即使在指定了初始容量之后,后续如果有需要,也可以自动扩容。所以我们可以根据是否需要扩容来选取合适的队列。

从内存结构角度考虑选择阻塞队列

  • ArrayBlockingQueue:数组的形式。没有链表所需要的“节点”,空间利用率更高。所以如果我们对性能有要求可以从内存的结构角度去考虑这个问题。
  • LinkedBlockingQueue:链表的形式。

从性能角度考虑选择阻塞队列

  • 比如 LinkedBlockingQueue 由于拥有两把锁,它的操作粒度更细,在并发程度高的时候,相对于只有一把锁的 ArrayBlockingQueue 性能会更好。
  • SynchronousQueue 性能往往优于其他实现,因为它只需要“直接传递”,而不需要存储的过程。如果我们的场景需要直接传递的话,可以优先考虑 SynchronousQueue。

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL有非常深入的了解
  • 关注公众号,每天持续高效的了解并发编程!
  • 关注公众号,后续持续高效的了解spring源码!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-02-04 21:36  程序java圈  阅读(45)  评论(0编辑  收藏  举报