最近发现几起对threadpoolexecutor的误用,其中包括自己,发现都是因为没有仔细看注释和内部运转机制,想当然的揣测参数导致,先看一下新建一个threadpoolexecutor的构建参数:
public threadpoolexecutor(int corepoolsize,
int maximumpoolsize,
long keepalivetime,
timeunit unit,
blockingqueue<runnable> workqueue,
threadfactory threadfactory,
rejectedexecutionhandler handler)
看这个参数很容易让人以为是线程池里保持corepoolsize个线程,如果不够用,就加线程入池直至maximumpoolsize大小,如果还不够就往workqueue里加,如果workqueue也不够就用rejectedexecutionhandler来做拒绝处理。
但实际情况不是这样,具体流程如下:
1)当池子大小小于corepoolsize就新建线程,并处理请求
2)当池子大小等于corepoolsize,把请求放入workqueue中,池子里的空闲线程就去从workqueue中取任务并处理
3)当workqueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumpoolsize就用rejectedexecutionhandler来做拒绝处理
4)另外,当池子的线程数大于corepoolsize的时候,多余的线程会等待keepalivetime长的时间,如果无请求可处理就自行销毁
内部结构如下所示:
从中可以发现threadpoolexecutor就是依靠blockingqueue的阻塞机制来维持线程池,当池子里的线程无事可干的时候就通过workqueue.take()阻塞住。
其实可以通过executes来学学几种特殊的threadpoolexecutor是如何构建的。
public static executorservice newfixedthreadpool(int nthreads) {
return new threadpoolexecutor(nthreads, nthreads,
0l, timeunit.milliseconds,
new linkedblockingqueue<runnable>());
}
newfixedthreadpool就是一个固定大小的threadpool
public static executorservice newcachedthreadpool() {
return new threadpoolexecutor(0, integer.max_value,
60l, timeunit.seconds,
new synchronousqueue<runnable>());
newcachedthreadpool比较适合没有固定大小并且比较快速就能完成的小任务,没必要维持一个pool,这比直接new thread来处理的好处是能在60秒内重用已创建的线程。
其他类型的threadpool看看构建参数再结合上面所说的特性就大致知道它的特性