1. 了解线程池
1.1 WHAT:线程池是什么?
JDK1.8中的Javadoc中是这么说的:
An Executor that provides methods to manage termination and methods that can produce a Future for tracking progress of one or more asynchronous tasks.
提供一组方法可以对一个或者多个异步任务终止执行或者提供Future方法用于追踪执行进度
解决了两个不同的问题:
- 减少了每个任务的调用开销,通常在执行大量异步任务时可提供改进的性能。
- 绑定和管理执行一组集合任务时所消耗的资源(包括线程)。每个ThreadPoolExecutor还维护一些基本统计信息,例如已完成任务的数量。
总结一下,线程池内部维护了一个或者多个的worker,通过外部方法可提交任务到线程池中,开发者无需再自行管理线程的生命周期,使用线程池技术即可实现动态扩容。
1.2 HOW:怎么用线程池
JDK提供了三种线程池的实现:ThreadPoolExecutor,ForkJoinPool,ScheduledThreadPoolExecutor。分别介绍下三者的使用场景
-
ThreadPoolExecutor 也是ScheduledThreadPoolExecutor的父类。这是我们最常使用的实现,通常会使用Executors的工厂方法来获得新实例。重点介绍下该类的构造函数
public ThreadPoolExecutor(int corePoolSize,//核心线程数,核心线程通常不会被超时回收,除非设置allowsCoreThreadTimeOut为true int maximumPoolSize,//最大线程数,是指线程池中最多存在的线程个数 long keepAliveTime,//线程最大空闲时间,超时的线程会被回收,通常不会回收核心线程 TimeUnit unit, BlockingQueue<Runnable> workQueue,//工作队列,这里指定不同类型的队列使线程池呈现不同的执行结果 ThreadFactory threadFactory,//线程工厂对象,可以初始化线程的名称,group等 RejectedExecutionHandler handler) {//线程最多可以容许maximumPoolSize+workQueue.size()个任务,超出的会执行拒绝策略,内置的有4种:直接执行、中断异常、忽略、剔除旧任务 .... }
-
ScheduledThreadPoolExecutor 可以设置任务在给定的延迟后运行或定期执行。当需要多个工作线程,或者需要ThreadPoolExecutor(此类扩展)的附加灵活性或功能时,此类比Timer更好。由于是ThreadPoolExecutor的子类,所以该类的构造方法内部其实是调用了父类的构造方法,需要注意的是队列类型使用的是DelayedWorkQueue类型,
public ScheduledThreadPoolExecutor(int corePoolSize,//核心线程同上 ThreadFactory threadFactory,//线程工厂对象,可以初始化线程的名称,group等 RejectedExecutionHandler handler) {// super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue(), threadFactory, handler); }
-
ForkJoinPool 此类比较特殊,当你需要把一个大任务分解为多个小任务并行执行时,并将多个结果汇总,可以使用这个线程池。类似于MapReduce分而治之的思路,内部使用work-stealing工作机制,不同线程默认执行自己的任务,在空闲下来时也可执行别的线程的任务,Stream中的ParallelStreams方法也用到了该类。