阻塞队列有哪些?
在 Java 的 java.util.concurrent 包里面,阻塞队列的实现挺多的,我们可以根据它的功能和结构来记,主要分这么几类:
1. 按容量划分
- 有界队列:就是队列有固定的容量。
ArrayBlockingQueue:最经典的一个,底层是数组,创建时必须指定大小。它的生产和消费用同一把锁,性能相对稳定。LinkedBlockingQueue:底层是链表,它既可以是有界的(构造时指定容量),也可以默认是无界的(默认是Integer.MAX_VALUE,几乎相当于无界)。它的生产和消费用了两把锁,在高并发场景下吞吐量通常比ArrayBlockingQueue更高。
- 无界队列:理论上是无限的,只要内存够就能一直放。
PriorityBlockingQueue:一个支持优先级排序的无界队列。元素必须实现Comparable接口,或者构造时传入Comparator。它出队的顺序是按优先级来的,不是先进先出。DelayQueue:一个很特殊的队列,里面放的是实现了Delayed接口的元素。每个元素都有个到期时间,只有到期了的元素才能被取出来。典型应用就是做缓存过期、定时任务调度。
2. 特殊功能的队列
SynchronousQueue:这个队列非常特别,它不存储元素。每一个put操作必须等待一个take操作,就像'手递手'交接一样。它直接传递任务,效率很高,常用于线程池之间直接传递工作。CachedThreadPool 用的就是它。LinkedTransferQueue:是LinkedBlockingQueue和SynchronousQueue的结合体。它多了个transfer方法,如果当前有消费者在等待,就直接把元素给消费者;如果没有,就入队,并且会阻塞直到该元素被消费掉。性能很好。
所以,比较常记的是 ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue 和 SynchronousQueue 这几个,它们各有各的应用场景。
拒绝策略有哪些?
拒绝策略是线程池的一个组成部分。当线程池的工作队列满了,并且所有线程也都达到最大线程数了,这时候再来新任务,就会触发拒绝策略。Java 在 ThreadPoolExecutor 类里提供了 4 种内置的策略,都实现了 RejectedExecutionHandler 接口:
1. AbortPolicy(中止策略 - 默认策略)
- 做法:直接抛出一个
RejectedExecutionException异常。 - 感受:比较粗暴,但好处是能让我们及时感知到系统出了饱和问题。
2. CallerRunsPolicy(调用者运行策略)
- 做法:不抛弃任务,也不抛异常,而是将任务回退给调用者(提交任务的线程),让调用者自己去执行这个任务。
- 感受:这是一种'负反馈'机制。提交任务的线程突然要自己去干活,它就忙起来了,自然就慢下来提交新任务了,给了线程池一个喘息的机会。这个策略在生产环境挺实用的,能平滑地降低流量。
3. DiscardPolicy(丢弃策略)
- 做法:默默地把新提交的任务丢弃掉,不执行,也不给任何通知。


