线程池的创建
ThreadPoolExecutor 的构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
字段 | 含义 |
---|---|
corePoolSize | 核心线程池大小 |
maximumPoolSize | 线程池最大大小 |
keepAliveTime | 超过核心数量时,多长时间开始回收空闲线程 |
unit | 时间单位 |
workQueue | 任务队列 |
threadFactory | |
handler | 拒绝策略 |
阿里开发规范中建议不要使用例如 Executors.newFixedThreadPool(10) 这种方式创建线程池,建议直接用构造方法创建线程池,明确创建线程池的目的
线程池的执行逻辑是,
1.有任务来时,如果没有线程处理,就创建线程直到corePoolSize数量为止
2.再有任务过来,则放入队列中
3.当队列满了则创建线程,直到maximumPoolSize,但这一部分线程在keepAliveTime的空闲时间后会回收,这是线程池内会一直保持这corePoolSize数量的线程
所以就有,
如果corePoolSize == maximumPoolSize 则线程池在队列满了不会在创建线程,且`keepAliveTime`就失去了意义
如果workQueue 是一个无限大小的队列,例如linkedqueue则永远填不满,直到oom也不会再创建新的线程,所以corePoolSize初始化太小,并且用linkedqueue的情况会导致没有足够的线程处理任务
所以建议根据实际情况调节corePoolSize,maximumPoolSize,workQueue的大小
拒绝策略(队列满了会如何处理,所以使用无限大小的队列这个参数就没有意义了)
DiscardPolicy (多余的任务,直接丢弃。相当于丢弃丢列尾部的任务)
AbortPolicy (报错)
DiscardOldestPolicy(丢弃最老的任务,相当于丢弃队列头部的任务)
还有其他两种,具体请看 RejectedExecutionHandler接口的实现类,逻辑很简单。
批量执行
ExecutorService.invokeAll()方法接收一个list<Callable>,当全部任务都执行完成后返回List<Future<T>> futures
并且这种方式是能保证请求和返回值的一一对应关系,顺序是一致的
批量执行时有种情况
当批量执行的任务数量大于线程池数量+队列数量,这时根据拒绝策略会有不同情况讨论
AbortPolicy 这时会将报错后的任务以及前面没执行完的任务取消
DiscardPolicy,DiscardOldestPolicy 这两种情况会导致线程池锁住 ( 重要 )
**因为invokeAll方法上会调用future.get方法,且是堵塞的,如果任务被丢弃了,则会永远堵塞在这里**
'''
if (tasks == null)
throw new NullPointerException();
List<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
for (Future<T> f : futures) {
if (!f.isDone()) {
try {
f.get(); <-------- ,如果任务被丢弃,这里会堵住
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done)
for (Future<T> f : futures)
f.cancel(true);
}
'''