线程池介绍及其使用
2019-08-25 本文已影响0人
jumper996
1. ThreadPoolExecutor
1.1 声明
public class ThreadPoolExecutor extends AbstractExecutorService
1.2 类的继承关系
image.png上图可以看出,他的顶层是一个Executor的接口。
1.3 Executor 源码查看
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
Executor 声明中说到,执行提交的{Runnable}任务的对象。这个接口提供了一种将任务提交与每个任务运行方式的机制分离的方法,包括线程使用,调度等细节。{Executor}通常用于执行任务,而不是显式创建线程。
他希望你的没一组任务不需要用以下方式来实现
new Thread(new RunnableTask()).start();
而是期望执行的方式是
Executor executor = new Executor();
executor.execute(new RunnableTask1());
executor.execute(new RunnableTask2());
他期望的就是复用线程,不需要每一个任务都去创建一个线程。
1.4 ExecutorService 源码查看
Executor只是提供了一个任务执行的行为,ExecutorService则是为Executor拓展一些服务功能,例如,查看状态,关闭服务。
*/
public interface ExecutorService extends Executor {
/**
* 启动有序关闭,其中先前提交的任务被执行,但不会接受任何新任务。 如果已经关闭,调用没有额外的影响。
*/
void shutdown();
/**
* 尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。
*/
List<Runnable> shutdownNow();
/**
* 是否关闭
*/
boolean isShutdown();
/**
* 判断是否终止,在所有任务执行完成以后返回true。
* 该方法只有在你调用了shutdown或者shutdownNow之后才会为true,否则就是false。
*/
boolean isTerminated();
/**
* 阻止所有任务,在关闭请求之后,或发生超时,或者当前线程被中断,之后执行。
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 提交一个任务,并返回指定类型的任务执行结果。Callable跟Runnable差不多,Callable的call方法可能抛出异常。
*/
<T> Future<T> submit(Callable<T> task);
/**
* 提交一个任务,并返回指定类型的任务执行结果。
*/
<T> Future<T> submit(Runnable task, T result);
/**
* 提交一个任务,返回任务执行结果。
*/
Future<?> submit(Runnable task);
/**
* 执行所有任务,返回已成功完成的任务的结果
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
/**
* 执行所有任务,返回已成功完成的任务的结果,可设置超时时间
*/
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
/**
* 执行给定集合的任意一个任务,返回已成功完成的任务的结果
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
/**
* 执行给定集合的任意一个任务,返回已成功完成的任务的结果,可设置超时时间
*/
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
1.5 ThreadPoolExecutor使用
1.5.1 基本参数说明
1、corePoolSize:核心线程数
* 核心线程会一直存活,及时没有任务需要执行
* 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
* 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
2、queueCapacity:任务队列容量(阻塞队列)
* 当核心线程数达到最大时,新任务会放在队列中排队等待执行
3、maxPoolSize:最大线程数
* 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
* 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
4、 keepAliveTime:线程空闲时间
* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
* 如果allowCoreThreadTimeout=true,则会直到线程数量=0
5、allowCoreThreadTimeout:允许核心线程超时
6、rejectedExecutionHandler:任务拒绝处理器
* 两种情况会拒绝处理任务:
- 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
- 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
* 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
* ThreadPoolExecutor类有几个内部实现类来处理这类情况:
- AbortPolicy 丢弃任务,抛运行时异常
- CallerRunsPolicy 执行任务
- DiscardPolicy 忽视,什么都不会发生
- DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
* 实现RejectedExecutionHandler接口,可自定义处理器
1.5.2 任务队列
WordQueue任务队列,用于转移和阻塞提交了的任务,即任务队列是运行线程的,任务队列根据corePoolSize和maximumPoolSize工作:
1.当正在运行的线程小于corePoolSize,线程池会创建新的线程
2.当大于corePoolSize而任务队列未满时,就会将整个任务塞入队列
3.当大于corePoolSize而且任务队列满时,并且小于maximumPoolSize时,就会创建新额线程执行任务
4.当大于maximumPoolSize时,会根据handler策略处理线程
任务队列有以下三种模式:
1. 直接提交:工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。
在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。
此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes
以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
2.无界队列:使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。
这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,
即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列
所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
3.有界队列:当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。
队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,
但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。
使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
未完待续。。。
参考:
Queue基本分析
CAS
ThreadPoolExecutor
线程池配置