api reference:https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize-即便他们空闲,池中保持的线程数,除非设计了allowCoreThreadTimeOut
maximumPoolSize-最大线程数
keepAliveTime-线程数超过core,其余的等待新任务的空闲线程销毁前等待的最大时间
unit-keepAliveTime参数的时间单位
workQueue-在执行前存放任务的队列。队列仅存放execute方法提交的Runnable
threadFactory-用于产生新线程
handler-当线程数量和队列容量到达时,处理堵塞的策略
当一个新任务提交并且线程数低于corePoolSize时,创建新线程处理请求,无论其他线程是否空闲。如果比corePoolSize多比maximumPoolSize少,只有队列满才会创建新线程。设置两个size相同,创建了一个固定数量线程池。设置max到类似MAX_VALUE的无边界值,允许池容纳任意数量并发任务。可以被动态改变,使用setCorePoolSize(int)/setMaximumPoolSize(int)
默认情况,当新任务来时core线程才会创建和启用,但可以被prestartCoreThread()/prestartAllCoreThreads()动态覆盖
新线程用ThreadFactory创建。默认使用Executors.defaultThreadFactroy(),创建的线程全部在相同的ThreadGroup/相同的NORM_PRIORITY优先级/非daemon状态。通过用不同的ThreadFactory,可以修改线程名,线程组,优先级,daemon状态。
如果池有超过coresize线程,当额外线程的空闲时间超过keepAliveTime将会被中止。这可以在池不活跃时减少资源使用。使用大数值可以避免空闲线程被中止。默认情况policy只用于多余coresize。allowCoreThreadTimeout(boolean)可以把policy用于core threads
queuing的三种常见策略:
- 直接推开。SynchronousQueue,将任务直接交给线程,不持有。如果没有线程能立即运行,入队任务会失败,新线程会被创建。处理有内部依赖的一些请求时,该policy避免了锁定。直接推开一般需要无界maxsize来避免拒绝新提交任务。当命令来的速度快于被处理的速度时,这允许无界线程增长的可能
- 无界queues.LinkedBlockingQueue不带预定义容量。使用无界队列将导致,所有core thread繁忙时,新任务入队等待。因此,没有更多线程被创建,maxSize无用。当每个任务完全独立时适用,任务不影响其他执行,比如web page server。也可以用于平滑请求,允许无界队列增长的可能,当命令持续到来的速度快于他们被处理的速度。
- 有界队列。ArrayBlockingQueue。当使用有限maxSIze时帮助阻止资源耗尽,较难调整和控制。queue size和maxSize需要权衡:大queue小pool使CPU/OS资源/上下文切换成本最低,也带来低吞吐量。如果任务频繁block(比如IO),系统可能能为更多线程调度时间。小queue大pool,使CPU繁忙,但有不可接受的调度成本,也降低了吞吐量
当Executor被关闭,使用了有限maxSize和queue容量,新提交任务会被reject。4种预定义处理polcy:
- default AboutPolicy,throws a runtime RejectedExecutionExecption
- CallerRunsPlicy,调用execute的线程执行任务,提供了一个简单的反馈控制机制,用于降低新任务提交的频率
- DiscardPolicy,无法执行的任务简单丢掉
- DiscardOldestPolicy,queue头部任务被丢掉
beforeExecute(Thread,Runnable) afterExecute(Runnable,Throwable),在每个任务执行前后被调用,可用于管理执行环境,比如,重初始化ThreadLocals,收集静态信息,log。terminated()可被重载用于执行特殊过程
getQueue()允许为monitor和debug访问queue,其他目的不推荐。remove(Runnable),purge()可用于取消大量任务来回收内存
Executor4个便利的工厂方法
线程无界
队列无界
池的好处:降低资源消耗,提高响应速度,提高线程可管理性
1.主线程首先要创建实现 Runnable 或者 Callable 接口的任务对象。
2.把创建完成的实现 Runnable/Callable接口的 对象直接交给 ExecutorService 执行: ExecutorService.execute(Runnable command))或者也可以把 Runnable 对象或Callable 对象提交给 ExecutorService 执行(ExecutorService.submit(Runnable task)或 ExecutorService.submit(Callable task))。
3.如果执行 ExecutorService.submit(…),ExecutorService 将返回一个实现Future接口的对象(我们刚刚也提到过了执行 execute()方法和 submit()方法的区别,submit()会返回一个 FutureTask 对象)。由于 FutureTask 实现了 Runnable,我们也可以创建 FutureTask,然后直接交给 ExecutorService 执行。
4.最后,主线程可以执行 FutureTask.get()方法来等待任务执行完成。主线程也可以执行 FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。