介绍 Java 并发包里的几个主要 ExecutorService 。html
CachedThreadPool 是经过 java.util.concurrent.Executors 建立的 ThreadPoolExecutor 实例。这个实例会根据须要,在线程可用时,重用以前构造好的池中线程。这个线程池在执行 大量短生命周期的异步任务时(many short-lived asynchronous task),能够显著提升程序性能。调用 execute 时,能够重用以前已构造的可用线程,若是不存在可用线程,那么会从新建立一个新的线程并将其加入到线程池中。若是线程超过 60 秒还未被使用,就会被停止并从缓存中移除。所以,线程池在长时间空闲后不会消耗任何资源。java
注意队列实例是:new SynchronousQueue
/** * Creates a thread pool that creates new threads as needed, but * will reuse previously constructed threads when they are * available. These pools will typically improve the performance * of programs that execute many short-lived asynchronous tasks. * Calls to <tt>execute</tt> will reuse previously constructed * threads if available. If no existing thread is available, a new * thread will be created and added to the pool. Threads that have * not been used for sixty seconds are terminated and removed from * the cache. Thus, a pool that remains idle for long enough will * not consume any resources. Note that pools with similar * properties but different details (for example, timeout parameters) * may be created using {@link ThreadPoolExecutor} constructors. * * @return the newly created thread pool */ public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
FixedThreadPool 是经过 java.util.concurrent.Executors 建立的 ThreadPoolExecutor 实例。这个实例会复用 固定数量的线程 处理一个 共享的无边界队列 。任什么时候间点,最多有 nThreads 个线程会处于活动状态执行任务。若是当全部线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。若是任何线程在执行过程当中由于错误而停止,新的线程会替代它的位置来执行后续的任务。全部线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。缓存
注意队列实例是:new LinkedBlockingQueue
/** * Creates a thread pool that reuses a fixed number of threads * operating off a shared unbounded queue. At any point, at most * <tt>nThreads</tt> threads will be active processing tasks. * If additional tasks are submitted when all threads are active, * they will wait in the queue until a thread is available. * If any thread terminates due to a failure during execution * prior to shutdown, a new one will take its place if needed to * execute subsequent tasks. The threads in the pool will exist * until it is explicitly {@link ExecutorService#shutdown shutdown}. * * @param nThreads the number of threads in the pool * @return the newly created thread pool * @throws IllegalArgumentException if {@code nThreads <= 0} */ public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
SingleThreadPool 是经过 java.util.concurrent.Executors 建立的 ThreadPoolExecutor 实例。这个实例只会使用单个工做线程来执行一个无边界的队列。(注意,若是单个线程在执行过程当中由于某些错误停止,新的线程会替代它执行后续线程)。它能够保证认为是按顺序执行的,任什么时候候都不会有多于一个的任务处于活动状态。和 newFixedThreadPool(1) 的区别在于,若是线程遇到错误停止,它是没法使用替代线程的。多线程
/** * Creates an Executor that uses a single worker thread operating * off an unbounded queue. (Note however that if this single * thread terminates due to a failure during execution prior to * shutdown, a new one will take its place if needed to execute * subsequent tasks.) Tasks are guaranteed to execute * sequentially, and no more than one task will be active at any * given time. Unlike the otherwise equivalent * <tt>newFixedThreadPool(1)</tt> the returned executor is * guaranteed not to be reconfigurable to use additional threads. * * @return the newly created single-threaded Executor */ public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
LiftOff并发
public class LiftOff implements Runnable { protected int countDown = 10; // Default private static int taskCount = 0; private final int id = taskCount++; public LiftOff() {} public LiftOff(int countDown) { this.countDown = countDown; } public String status() { return "Thread ID: [" + String.format("%3d", Thread.currentThread().getId()) + "] #" + id + "(" + (countDown > 0 ? countDown : "LiftOff!") + ") "; } @Override public void run() { while (countDown-- > 0) { System.out.println(status()); Thread.yield(); } } }
CachedThreadPoolCaseless
public class CachedThreadPoolCase { public static void main(String[] args) throws InterruptedException { ExecutorService exec = Executors.newCachedThreadPool(); for(int i = 0; i < 5; i++) { exec.execute(new LiftOff()); Thread.sleep(10); } exec.shutdown(); } }
当 sleep 间隔为 5 milliseconds 时,共建立了 3 个线程,并交替执行。异步
当 sleep 间隔为 10 milliseconds 时,共建立了 2 个线程,交替执行。async
FixedThreadPoolCase
public class FixedThreadPoolCase { public static void main(String[] args) throws InterruptedException { ExecutorService exec = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) { exec.execute(new LiftOff()); Thread.sleep(10); } exec.shutdown(); } }
不管 sleep 间隔时间是多少,总共都建立 3 个线程,并交替执行。
SingleThreadCase
public class SingleThreadPoolCase { public static void main(String[] args) throws InterruptedException { ExecutorService exec = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { exec.execute(new LiftOff()); Thread.sleep(5); } exec.shutdown(); } }
不管 sleep 间隔时间是多少,总共都只建立 1 个线程。
特性 | FixedThreadPool | CachedThreadPool |
---|---|---|
重用 | FixedThreadPool 与 CacheThreadPool差很少,也是能 reuse 就用,但不能随时建新的线程 | 缓存型池子,先查看池中有没有之前创建的线程,若是有,就 reuse ;若是没有,就建一个新的线程加入池中 |
池大小 | 可指定 nThreads,固定数量 | 可增加,最大值 Integer.MAX_VALUE |
队列大小 | 无限制 | 无限制 |
超时 | 无 IDLE | 默认 60 秒 IDLE |
使用场景 | 因此 FixedThreadPool 多数针对一些很稳定很固定的正规并发线程,多用于服务器 | 大量短生命周期的异步任务 |
结束 | 不会自动销毁 | 注意,放入 CachedThreadPool 的线程没必要担忧其结束,超过 TIMEOUT 不活动,其会自动被终止。 |
FixedThreadPool 和 CachedThreadPool 二者对高负载的应用都不是特别友好。
CachedThreadPool 要比 FixedThreadPool 危险不少。
若是应用要求高负载、低延迟,最好不要选择以上两种线程池:
CachedThreadPool
在线程建立上失控由于二者都不是特别友好,因此推荐使用 ThreadPoolExecutor
,它提供了不少参数能够进行细粒度的控制。
RejectionHandler
- 自定义的 RejectionHandler
或 JDK 提供的默认 handler 。若是在任务完成先后须要执行某些操做,能够重载
beforeExecute(Thread, Runnable) afterExecute(Runnable, Throwable)
在运行时动态控制线程池的大小(Dynamic Thread Pool)
iteye: Java 并发包中的几种 ExecutorService
stackoverflow: FixedThreadPool vs CachedThreadPool: the lesser of two evils
blogjava: 深刻浅出多线程(4)对CachedThreadPool OutOfMemoryError问题的一些想法