系列文章:ThreadPoolExecutor的完整构造方法的签名是: ThreadPoolExecutor( int corePoolSize, // 线程池维护线程的最少数量 int maximumPoolSize, // 线程池维护线程的最大数量 long keepAliveTime, // 线程池维护线程所允许的空闲时间 TimeUnit unit, // 线程池维护线程所允许的空闲时间的单位 BlockingQueue<Runnable> workQueue, // 线程池所使用的缓冲队列 ThreadFactory threadFactory, RejectedExecutionHandler handler // 线程池对拒绝任务的处理策略 ) 线程池的工作过程 1. 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。 2. 当调用 execute() 方法添加一个任务时,线程池会做如下判断: a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务; 3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。 4. 当一个线程空闲,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。 ThreadPoolExecutor是Executors类的底层实现 在JDK帮助文档中,有如此一段话: 强烈建议程序员使用较为方便的 Executors 工厂方法 Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和 Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } ExecutorService newSingleThreadExecutor():单线程 public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } ExecutorService newCachedThreadPool():无界线程池,可以进行自动线程回收 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } BlockingQueue 1. SynchronousQueue 一种阻塞队列,其中每个 put 必须等待一个 take,反之亦然,即其的操作必须是放和取交替完成的。同步队列没有任何内部容量,甚至连一个队列的容量都没有。不能在同步队列上进行 peek,因为仅在试图要取得元素时,该元素才存在;除非另一个线程试图移除某个元素,否则也不能(使用任何方法)添加元素;也不能迭代队列,因为其中没有元素可用于迭代。队列的头是尝试添加到队列中的首个已排队线程元素;如果没有已排队线程,则不添加元素并且头为 null。对于其他Collection 方法,SynchronousQueue 作为一个空集合。此队列不允许 null 元素。 2. LinkedBlockingQueue 一个基于链表的、范围任意的 blocking queue。此队列按 FIFO(先进先出)排序元素。队列的头部是在队列中时间最长的元素。队列的尾部是在队列中时间最短的元素。新元素插入到队列的尾部,并且队列检索操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。 3. ArrayBlockingQueue ArrayBlockingQueue是一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部是在队列中存在时间最长的元素。队列的尾部是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。 这是一个典型的"有界缓存区",固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致放入操作受阻塞;试图从空队列中检索元素将导致类似阻塞。 4. PriorityBlockingQueue 一个无界的阻塞队列,它使用与类 PriorityQueue 相同的顺序规则,并且提供了阻塞检索的操作。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会失败(导致 OutOfMemoryError)。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象。 5. DelayQueue Delayed 元素的一个无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。当一个元素的getDelay(TimeUnit.NANOSECONDS)方法返回一个小于或等于零的值时,则出现期满。此队列不允许使用 null 元素。 6. BlockingDque + LinkedBlockingQueue BlockingDque为阻塞双端队列接口,实现类有LinkedBlockingDque。双端队列特别之处是它首尾都可以操作。LinkedBlockingDque不同于LinkedBlockingQueue,它只用一个lock来维护读写操作,并由这个lock实例化出两个Condition notEmpty及notFull,而LinkedBlockingQueue读和写分别维护一个lock。 RejectedExecutionHandler RejectedExecutionHandler接口提供了对于拒绝任务的处理的自定方法的机制。在ThreadPoolExecutor中已经默认包含了4中策略: CallerRunsPolicy 线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。 public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { if (!e.isShutdown()) { r.run(); } } 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); } } 转载请并标注: “本文转载自 linkedkeeper.com ” ©著作权归作者所有 |