ThreadPoolExecutor是一个ExecutorService ,使用可能的几个合并的线程执行每个提交的任务,通常使用Executors工厂方法配置,通过Executors可以配置多种适合不同场景的线程池。
ThreadPoolExecutor中的主要参数
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
corePoolSize
线程池中的核心线程数,当外部提交一个任务时,线程池就创建一个新线程执行任务,直到当前线程数等于corePoolSize时不再创建新线程;
如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;
如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。
maximumPoolSize
线程池中允许的最大线程数。如果当前阻塞队列已满,还在继续提交任务,则创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。
keepAliveTime
线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间。默认情况下,线程一般不会被销毁,该参数只在线程数大于corePoolSize时才有用。
workQueue
workQueue必须是阻塞队列。当线程池中的线程数超过corePoolSize的时候,线程会进入阻塞队列进行等待。阻塞队列可以使有界的也可以是无界的。
threadFactory
创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个线程名。Executors静态工厂里默认的threadFactory,线程的命名规则是“pool-{数字}-thread-{数字}”。
RejectedExecutionHandler
线程池的饱和处理策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
•AbortPolicy:直接抛出异常,默认的处理策略
•CallerRunsPolicy:使用调用者所属的线程来执行当前任务
•DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务
•DiscardPolicy:直接丢弃该任务
如果上述提供的处理策略无法满足业务需求,也可以根据场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
ThreadPoolExecutor中的主要执行流程
//图片来自网络
1.线程池判断核心线程池里的线程(corePoolSize)是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入2。
2.线程池判断工作队列(workQueue)是否已满。如果工作队列没有满,则将新提交的任务存储在该队列里。如果工作队列满了,则进入3。
3.线程池判断线程池的线程(maximumPoolSize)是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。
这里需要注意的是核心线程池大小指得是corePoolSize参数,而线程池工作线程数指的是maximumPoolSize。
Executor
实际上我们在使用线程池时,并不一定需要自己来定义上面介绍的参数的值,JDK为我们提供了一个调度框架。通过这个调度框架我们可以轻松的创建好线程池以及异步的获取任务的执行结果。
调度框架的组成
任务
一般是指需要被执行的任务,多为使用者提供。被提交的任务需要实现Runnable接口或Callable接口。
任务的执行
Executor是任务执行机制的核心接口,其将任务的提交和执行分离开来。ExecutorService继承了Executor并做了一些扩展,可以产生Future为跟踪一个或多个异步任务执行。任务的执行主要是通过实现了Executor和ExecutorService接口的类来进行实现。例如:ThreadPoolExecutor和ScheduledThreadPoolExecutor。
结果获取
对结果的获取可以通过Future接口以及其子类接口来实现。Future接口提供了一系列诸如检查是否就绪,是否执行完成,阻塞以及获取结果等方法。
Executors工厂中的线程池
FixedThreadPool
new ThreadPoolExecutor(nThreads, nThreads, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
该线程池中corePoolSize和maximumPoolSize参数一致。同时使用无界阻塞队列,将会导致maximumPoolSize和keepAliveTime已经饱和策略无效,因为队列会一直接收任务,直到OOM。
SingleThreadExecutor
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>())