ThreadPoolExcutor是java并发模块中非常重要的一个线程池的实现,FixedThreadPool,CachedThreadPool和SingleThreadPool都是该pool的一个不通的参数情况下的特性。本文主要介绍一下ThreadPoolExcutor的关键参数以及常用的方法的内部实现。
1. 构造函数及关键参数
下面的代码是ThreadPoolExecutor的构造函数,这里涉及了多个相关的参数,其中corePoolSize和maximumPoolSize两个参数共同控制了线程池中线程的大小。
* corePoolSize表示的是线程池中能够长期贮存的线程数量,即使某些线程是闲置状态,
* maximumPoolSize顾名思义表示线程池中能够存在的最大线程数量。
* keepAliveTime是线程数大于corePoolSize的时候,闲置线程能够存活的时间
* BlockingQueue < Runnable > 是其内部维护的等待被执行的工作队列。
除此之外,ThreadPoolExcutor内部使用的是HashSet< Worker >去维护其线程的。
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
2. 关键方法之execute()
我们在使用java的并发框架的提交任务的时候,一般会用到submit方法和execute方法两种, 其中submit方法的内部是调用execute方法的,因此在这只对execute()方法进行分析。
从下面的源码中可以看到这里有三个关键的步骤:
1. 当当前的worker的数量(也就是thread, Worker是Thread的封装)少于corePoolSize的时候,新建一个Worker,并将当前的任务command作为该worker的第一个任务。
2. 否则的话,理论上需要将command添加到任务队列中去,这里源码做了两次的check(), 这主要是基于两个原因:1)之前如果是有线程跪了,哪么需要再启动新的线程,2)在此期间调用了shutdown此时应该拒绝该command加入任务队列。
3. 如果不能将command加入工作队列(工作队列已满), 那么在尝试新建线程(这里读者可以仔细看一下两个addWorker的方法的不同,前一个第二个参数true表示线程数上限为corePoolSize后者则表示是maximumPoolSize),如果此时新建线程失败,则有可能是线程池处于饱和状态或者是调用了shutdown方法。
至此execute的方法就结束了,总结一下就是首先尝试在当前线程数小于corePoolSize的情况下新建线程,不成功则尝试将任务添加到任务队列,如果仍然不成功则尝试在maximumPoolSize的线程上界下新建线程,如果任然不成功则拒绝。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
3. 关键方法之runWorker
该方法是Worker内部调用的方法,也是每个线程执行的主要方法体,该方法所做的事情很简单,就是不断地从任务队列中去取job,然后执行job。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
4. 关键方法之shutdown, shutdownNow, awaitTermination
着三个方法都是线程关闭的相关方法,
* shutdown方法调用之后,线程池将会拒绝新的任务进来,但是它并不等待已经提交方法的结束,awaitTermination则会等待提交任务的结束。
* shutdownNow 将会尝试去结束所有正在执行的任务,并从队列清除等待任务,将这些等待任务作为返回值返回,shutdownNow方法也不会去等正在执行任务的完全关闭。
代码如下:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
5. 其他
ThreadPoolExecutor主体就介绍完了,接下来介绍一下如何利用ThreadPoolExecutor去构建CachedThreadPool,SingleThreadPool以及FixThreadPool。 Executors中相关代码如下:
1. CachedThreadPool中corePoolSize = 0, maximumPoolSize = Integer.MAX_VALUE, KeepAliveTime = 60 sec, 这也就是说CachedThreadPool允许按需建立新的线程,闲置的线程存活时间为60秒,因此比较适合短任务的情况这样能够复用线程。
2. SingleThreadPool 最大只能够允许1个线程的存在,当前线程跪了的情况下是允许新的线程建立并执行工作的,由于corePoolSize和maximumPoolSize的数量相同对于keepAliveTime来说多大也就没有意义
3. FixedThreadPool 的参数中corePoolSize和maximumPoolSize的数量相同,这也就固定了线程池中的线程数的大小。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}