阿里巴巴的java开发规范上说线程池要自己用new创建,方便开发理解线程池的各个参数。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
参数介绍:
corePoolSize 核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
maximumPoolSize 指的是线程池的最大大小(线程池中最大有corePoolSize 个线程可运行)。
keepAliveTime 指的是空闲线程结束的超时时间(当一个线程不工作时,过keepAliveTime 长时间将停止该线程)。
unit 是一个枚举,表示 keepAliveTime 的单位(有NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS, MINUTES, HOURS, DAYS,7个可选值)。
workQueue 表示存放任务的队列(存放需要被线程池执行的线程队列)。
threadFactory 线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法
public interface ThreadFactory {
Thread newThread(Runnable r);
}
通过线程工厂可以对线程的一些属性进行定制,比如线程名。
handler 拒绝策略(添加任务失败后如何处理该任务).RejectedExecutionHandler是一个接口,只有一个方法
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
在这个方法中可以自己进行处理,比如日志记录被拒绝的任务。
运行原理
1、线程池刚创建时,里面没有一个线程。
2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a. 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b. 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列。
c. 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建线程运行这个任务;
d. 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会调用RejectedExecutionHandler的 rejectedExecution方法抛出异常。
3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行 的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
任务队列
SynchronousQueue
/**
* Always returns zero.
* A {@code SynchronousQueue} has no internal capacity.
*
* @return zero
*/
public int size() {
return 0;
}
/**
* Always returns zero.
* A {@code SynchronousQueue} has no internal capacity.
*
* @return zero
*/
public int remainingCapacity() {
return 0;
}
这个队列的size和remainingCapacity(可用数量)永远返回0,线程池认为等待队列已经满了。
就是说用这个队列,等待队列的size为0,有新的任务加入,会直接执行(用空闲线程或创建新的线程),如果线程数量达到max,那么线程池会抛出异常。
ArrayBlockingQueue 和 LinkedBlockingQueue
一个是数组实现,一个是链表实现,可以指定队列长度。
ArrayBlockingQueue和LinkedBlockingQueue是两个最普通、最常用的阻塞队列。
拒绝策略RejectedExecutionHandler
ThreadPoolExecutor中提供了几种策略可以使用:
- AbortPolicy
- DiscardPolicy
- DiscardOldestPolicy
- CallerRunsPolicy
AbortPolicy
该策略是线程池的默认策略。使用该策略时,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
DiscardPolicy
如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
DiscardOldestPolicy
将最早进入队列的任务删掉,再尝试加入队列。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
CallerRunsPolicy
使用此策略,如果添加到线程池失败,那么主线程(执行execute()方法的线程)会自己去执行该任务,不会等待线程池中的线程去执行。
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
自定义
如果以上策略都不符合需求,可以自己定义拒绝策略,实现RejectedExecutionHandler接口,并实现rejectedExecution方法就可以了。具体的逻辑就在rejectedExecution方法中定义。