1. 线程池概述
线程池将线程的执行和任务相分离,其中任务由Runnable接口和Callable接口及RunnableFuture接口组成。
2. 线程池基础
线程池的顶级接口Executor只有一个方法:execute(),这个方法执行任务。
ExecutorService扩展了Executor,增加了对线程池的控制及增强了线程的执行。
2.1 ExecutorService方法解析
- shutdown和shutdownNow区别及内部原理 shutdown:通知关闭线程池,不再添加任务,并且已经执行任务的线程池继续执行,执行完毕将关闭。 shutdownNow:立即关闭线程池(立即是多久),不再接受新任务,并且已经执行的任务立即停止(已经执行的任务真的会立即停止吗?)
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查是否有权限关闭线程池
checkShutdownAccess();
// 修改线程池状态为SHUTDOWN
advanceRunState(SHUTDOWN);
// 标记所有线程为中断状态
interruptIdleWorkers();
onShutdown(); // 没用,这个方法没有实现,保留目的是为了执行一些清理工作
} finally {
mainLock.unlock();
}
tryTerminate();
}
为什么通过shutdown()方法就可以做到不再添加任务,并且已经执行任务的线程池继续执行,执行完毕将关闭? 首先:不再添加新的任务,是通过在addWorker方法中检查线程池状态, interruptIdleWorkers将所有空闲的线程标记为中断状态,并且runWorker()方法中调用的getTask()因为检查了线程池状态,将不会再获取到新的任务。所有线程执行完毕后关闭。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 不再添加新的任务
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
探究这个问题,可以看看一个任务是如何被执行的。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 将状态标记为STOP
advanceRunState(STOP);
// 标记中断所有线程
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
- submit方法的三种不同的区别
submit三种方法的不同取决于参数的类型和是否有返回值,其中submit(Callable call)有返回值,submit(Runnable run)是否有返回值取决于Runnable,如果是RunnableFuture则有返回值。
- invokeAll方法内部实现原理 invokeAll代表批量提交执行任务,实际此方法并不实用。
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
if (tasks == null)
throw new NullPointerException();
ArrayList<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 (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
3. 线程池问题思考
- 线程池如何复用线程? 线程池复用线程的关键:线程池将每个线程包装成Worker类,Worker类继承AQS,在创建Worker时一般会指定第一个任务,后序通过循环getTask()获取任务,获取不到时根据策略关闭线程
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);
}
}
- 线程池的种类 线程池加入任务的逻辑: 1.核心线程->加入阻塞队列->创建非核心线程 核心线程根据allowCoreThreadTimeOut可以设置是否关闭超时关闭。
- 线程池高级用法
- 查看活跃线程数:getActiveCount()
- 查看总的线程数
- 查看任务总数:getTaskCount()
- 查看完成的任务总数:getCompletedTaskCount()