1、Executor
java se5的java.util.concurrent包中的执行器Executor,将为我们管理Thread对象,从而简化了并发编程。Executor在客户端和任务执行之间提供了一个间接层;与客户端直接执行任务不同,这个中介对象将执行任务。Executor允许你管理异步任务的执行,而无需显示的管理线程的生命周期。
2、CachedThreadPool
这个线程实例会根据需要,在线程可用时,重用之前构造好的池中线程。这个线程池在执行 大量短生命周期的异步任务时(many short-lived asynchronous task),可以显著提高程序性能。调用 execute 时,可以重用之前已构造的可用线程,如果不存在可用线程,那么会重新创建一个新的线程并将其加入到线程池中。如果线程超过 60 秒还未被使用,就会被中止并从缓存中移除。因此,线程池在长时间空闲后不会消耗任何资源。
3、FixedThreadPool
可以一次性预先执行代价高昂的线程分配,可以限制线程的数量。它可以节约时间,因为你不用为每个任务付出创建线程的开销。任何时间点,最多有 nThreads 个线程会处于活动状态执行任务。如果当所有线程都是活动时,有多的任务被提交过来,那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。
4、SingleThreadPool
就像是线程数量为1的FixedThreadPool,适合于连续运行的任何事物。如果想SingleThreadPool提供了多个任务,那么这些任务将排队,当现有任务结束后才开始下一个任务,所有任务使用相同的线程。其实是在维护自己的一份悬挂任务队列。和 newFixedThreadPool(1) 的区别在于,如果线程遇到错误中止,它是无法使用替代线程的。
5、FixedThreadPool 与 CachedThreadPool 特性对比
特性 | FixedThreadPool | CachedThreadPool |
---|---|---|
重用 | FixedThreadPool 与 CacheThreadPool差不多,也是能 reuse 就用,但不能随时建新的线程 | 缓存型池子,先查看池中有没有以前建立的线程,如果有,就 reuse ;如果没有,就建一个新的线程加入池中 |
池大小 | 可指定 nThreads,固定数量 | 可增长,最大值 Integer.MAX_VALUE |
队列大小 | 无限制 | 无限制 |
超时 | 无 IDLE | 默认 60 秒 IDLE |
使用场景 | 所以 FixedThreadPool 多数针对一些很稳定很固定的正规并发线程,多用于服务器 | 大量短生命周期的异步任务 |
结束 | 不会自动销毁 | 注意,放入 CachedThreadPool 的线程不必担心其结束,超过 TIMEOUT 不活动,其会自动被终止。 |
6、最佳实践
FixedThreadPool 和 CachedThreadPool 两者对高负载的应用都不是特别友好。
CachedThreadPool 要比 FixedThreadPool 危险很多。
如果应用要求高负载、低延迟,最好不要选择以上两种线程池:
- 任务队列的无边界:会导致内存溢出以及高延迟
- 长时间运行会导致
CachedThreadPool
在线程创建上失控
因为两者都不是特别友好,所以推荐使用 ThreadPoolExecutor
,它提供了很多参数可以进行细粒度的控制。
- 将任务队列设置成有边界的队列
- 使用合适的
RejectionHandler
- 自定义的RejectionHandler
或 JDK 提供的默认 handler 。 如果在任务完成前后需要执行某些操作,可以重载
beforeExecute(Thread, Runnable) afterExecute(Runnable, Throwable)
- 重载 ThreadFactory ,如果有线程定制化的需求
在运行时动态控制线程池的大小(Dynamic Thread Pool)