4.2.2 Executors工具类
可以自己实例化线程池,也可用Executors
创建线程池的工厂类,
常用方法
ExecutorService 的抽象类AbstractExecutorService提供了submit、invokeAll 等方法的实现,但是核心方法Executor.execute()并没有在这里实现。
因为所有的任务都在该方法执行,不同实现会带来不同的执行策略。
通过Executors的静态工厂方法可以创建三个线程池的包装对象
- ForkJoinPool
- ThreadPoolExecutor
- ScheduledThreadPoolExecutor
● Executors.newWorkStealingPool
JDK8 引入,创建持有足够线程的线程池支持给定的并行度,并通过使用多个队列减少竞争,构造方法中把CPU数量设置为默认的并行度。
返回ForkJoinPool ( JDK7引入)对象,它也是AbstractExecutorService 的子类
● Executors.newCachedThreadPool
创建一个无界的缓冲线程池,它的任务队列是一个同步队列。
任务加入到池中
- 若池中有空闲线程,则用空闲线程执行
- 若无,则创建新线程执行
池中的线程空闲超过60秒,将被销毁。线程数随任务的多少变化。
适用于执行耗时较小的异步任务。池的核心线程数=0 ,最大线程数= Integer.MAX_ _VALUE
maximumPoolSize 最大可至Integer.MAX_VALUE,是高度可伸缩的线程池。
若达到该上限,没有服务器能够继续工作,直接OOM。
keepAliveTime 默认为60秒;
工作线程处于空闲状态,则回收工作线程;
如果任务数增加,再次创建出新线程处理任务.
● Executors.newScheduledThreadPool
能定时执行任务的线程池。该池的核心线程数由参数指定,线程数最大至Integer.MAX_ VALUE,与上述一样存在OOM风险。
ScheduledExecutorService接口的实现类,支持定时及周期性任务执行;
相比Timer,ScheduledExecutorService 更安全,功能更强大.
与newCachedThreadPool的区别是不回收工作线程.
● Executors.newSingleThreadExecutor
创建一个单线程的线程池,相当于单线程串行执行所有任务,保证按任务的提交顺序依次执行.
只有-个线程来执行无界任务队列的单-线程池。该线程池确保任务按加入的顺序一个一
个依次执行。当唯一的线程因任务 异常中止时,将创建一个新的线程来继续执行 后续的任务。
与newFixedThreadPool(1)的区别在于,单线程池的池大小在newSingleThreadExecutor方法中硬编码,不能再改变的。
● Executors.newFixedThreadPool
创建一个固定大小任务队列容量无界的线程池
输入的参数即是固定线程数;
既是核心线程数也是最大线程数;
不存在空闲线程,所以keepAliveTime
等于0.
其中使用了 LinkedBlockingQueue, 但是没有设置上限!!!,堆积过多任务!!!
下面介绍LinkedBlockingQueue
的构造方法
使用这样的无界队列,若瞬间请求非常大,会有OOM风险。
除newWorkStealingPool 外,其他四个创建方式都存在资源耗尽的风险.
不推荐使用其中的任何创建线程池的方法,因为都没有任何限制,存在性能隐患。
Executors中默认的线程工厂和拒绝策略过于简单,通常对开发者不够友好。
线程工厂需要做创建前的准备工作,对线程池创建的线程必须明确标识,就像药品的生产批号一样,为线程本身指定有意义的名称和相应的序列号.
拒绝策略应该考虑到业务场景,返回相应的提示或者友好地跳转.
以下为简单的ThreadFactory 示例
上述示例包括线程工厂和任务执行体的定义;
通过newThread方法快速、统一地创建线程任务,强调线程一定要有特定意义的名称,方便出错时回溯.