线程池中的排队策略

ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

排队有三种通用策略:

直接提交。工作队列的默认选项是 SynchronousQueue,

它将任务直接提交给线程而不保持它们。如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务,此策
略允许无界线程具有增长的可能性。

无界队列。(不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的 maximumPoolSizes时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量

1. ArrayBlockingQueue类

       一个以数组为基础的有界阻塞队列,此队列按照先进先出原则对元素进行排序。队列头部元素是队列中存在时间最长的元素,队列尾部是存在时间最短的元素,新元素将会被插入到队列尾部。队列从头部开始获取元素。

       ArrayBlockingQueue是“有界缓存区”模型的一种实现,一旦创建了这样的缓存区,就不能再改变缓冲区的大小。

ArrayBlockingQueue的一个特点是,必须在创建的时候指定队列的大小。当缓冲区已满,则需要阻塞新增的插入操作,同理,当缓冲区已空,需要阻塞新增的提取操作。

       ArrayBlockingQueue是使用的是循环队列方法实现的,对ArrayBlockingQueue的相关操作的时间复杂度,可以参考循环队列进行分析



2 LinkedBlockingQueue

       一种通过链表实现的阻塞队列,支持先进先出。队列的头部是队列中保持时间最长的元素,队列的尾部是保持时间最短的元素。新元

素插入队列的尾部。可选的容量设置可以有效防止队列过于扩张造成系统资源的过多消耗,如果不指定队列容量,队列默认使用

Integer.MAX_VALUE。LinkedBlockingQueue的特定是,支持无限(理论上)容量。

3 PriorityBlockingQueue

    PriorityBlockingQueue是一种基于优先级进行排队的无界队列。队列中的元素按照其自然顺序进行排列,或者根据提供的Comparator进行排序,这与构造队列时,提供的参数有关。
  
使用提取方法时,队列将返回头部,具有最高优先级(或最低优先级,这与排序规则有关)的元素。如果多个元素具有相同的优先级,则同等优先级间的元素获取次序无特殊说明。

   优先级队列使用的是一种可扩展的数组结构,一般可以认为这个队列是无界的。当需要新添加一个元素时,如果此时数组已经被填满,优先队列将会自动扩充当前数组(一般认为是,先分配一个原数组一定倍数空间的数组,之后将原数组中的元素拷贝到新分配的数组中,释放原数组的空间)。

   如果使用优先级队列的iterator变量队列时,不保证遍历次序按照优先级大小进行。因为优先级队列使用的是堆结构。如果需要按照次序遍历需要使用Arrays.sort(pq.toArray())。关于堆结构的相关算法,请查考数据结构相关的书籍。

   在PriorityBlockingQueue的实现过程中聚合了PriorityQueue的一个实例,并且优先队列的操作完全依赖与PriorityQueue的实现。

在PriorityQueue中使用了一个一维数组来存储相关的元素信息。一维数组使用最小堆算法进行元素添加。

4 DelayQueue

       一个无界阻塞队列,只有在延时期满时才能从中提取元素。如果没有元素到达延时期,则没有头元素。


RejectedExecutionHandler

RejectedExecutionHandler接口提供了对于拒绝任务的处理的自定方法的机会。在ThreadPoolExecutor中已经默认包含了4中策略,因为源码

非常简单,这里直接贴出来。

CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

           if (!e.isShutdown()) {

               r.run();

           }

}

这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。

AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

           throw new RejectedExecutionException();

}

DiscardPolicy:不能执行的任务将被删除
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

}

DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

           if (!e.isShutdown()) {

               e.getQueue().poll();

               e.execute(r);

           }

}

该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。这个策略需要适当小心。设想:如果其他线程都还在运行,那么新来任务踢掉旧任务,缓存在queue中,再来一个任务又会踢掉queue中最老任务。

你可能感兴趣的:(线程池)