简介
ExecutorService执行任务,使用一个或多个池中的线程(使用Executors的工厂方法正常配置)。线程池解决两个问题:1.为大量的异步任务执行提高性能,提供了管理资源的一种方式(线程、执行任务的集合)。每一个ThreadPoolExecutor仍然提供了一些基础统计(完成任务的数量)。
编程人员通过Executors的newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor来获取最简单的线程池。
当一个新task通过execute方法提交后,线程池中核心线程数小于corePoolSize,一个新的线程将会创建,即使其它的工作线程是空闲的。如果线程数大于corePoolSize,小于maximumPoolSize,只有队列满的才会创建新的线程。
有三种通用的队列策略:直接处理SynchronousQueue,无界队列:LinkedBlockingQueue,有界队列:ArrayBlockingQueue
局部方法使用的风险
- 如果没有设置核心线程数,比如:newCachedThreadPool,在线程池的线程空闲时间到达60s后,线程会关闭,所有线程关闭后线程池也相应关闭回收。
- 如果设置的核心线程数,比如:newSingleThreadExecutor和newFixedThreadPool,如果没有主动关闭,或者设置核心线程的超时时间,核心线程会一直存在不会被关闭,这个线程池就不会被释放回收。线程池无法被回收,是因为线程池的引用被它的内部类Worker持有了,而Worker和线程一一对应,是对Thread的增强,所以本质上就是因为线程没有被释放。要执行线程退出需要两种情况:
- 线程池的状态->stop,需要调用shutdown或者shutdownnow方法
- getTask获取到空任务。有以下几种情况:当前线程数大于核心线程,会调用poll,超时后返回空任务;当前线程数小于等于核心线程,并且调用了allowCoreThreadTimeOut方法允许核心线程超时关闭的情况下,也是调用poll,超时后返回空任务。其他情况,调用take阻塞等待。正是调用了take阻塞导致核心线程迟迟不被关闭回收。
成员变量
private final BlockingQueue<Runnable> workQueue;
private final ReentrantLock mainLock = new ReentrantLock();
private final HashSet<Worker> workers = new HashSet<Worker>();
private final Condition termination = mainLock.newCondition();
private int largestPoolSize;
private long completedTaskCount;
private volatile ThreadFactory threadFactory;
private volatile RejectedExecutionHandler handler;
private volatile long keepAliveTime;
private volatile boolean allowCoreThreadTimeOut;
private volatile int corePoolSize;
private volatile int maximumPoolSize;
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
线程提交
Worker和Task的区别,Worker是当前线程池中的线程,而task虽然是runnable,但是并没有真正执行,只是被Worker调用了run方法,后面会看到这部分的实现。 maximumPoolSize和corePoolSize的区别:这个概念很重要,maximumPoolSize为线程池最大容量,也就是说线程池最多能起多少Worker。corePoolSize是核心线程池的大小,当corePoolSize满了时,同时workQueue full(ArrayBolckQueue是可能满的)。 那么此时允许新建Worker去处理workQueue中的Task,但是不能超过maximumPoolSize。超过corePoolSize之外的线程会在空闲超时后终止。