То, что ThreadPoolExecutor
завершение работы и выполнение задачи / удаление рабочей очереди / получение из рабочей очереди, является редкостью. Таким образом, вы не можете полагаться на механизм прерывания потока или что-то еще. Все, что вам гарантировано, это:
Инициирует упорядоченное завершение работы, при котором ранее представленные задачи
выполнено, но новые задачи не будут приняты. Призыв не имеет
дополнительный эффект, если он уже выключен.
Этот метод не ожидает завершения ранее отправленных задач
выполнение.
Чтобы углубиться в реализацию ThreadPoolExecutor
, давайте взглянем на основной метод выполнения:
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);
}
}
Важнейшая часть здесь - это getTask()
. Его фрагмент:
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
Метод не синхронизирован и зависит только от порядка, заданного значением CAS ctl
. ctl
здесь - глобальное состояние пула, хранящееся внутри AtomicInteger
(для неблокирующего получения атомарного ThreadPoolExecutor
состояния).
Так что возможен следующий случай.
- Рабочая нить называется
getTask
- Рабочий поток получил состояние выполнения пула. Это все еще
RUNNING
.
- Другой поток инициировал закрытие ордера и соответственно изменил
ctl
.
- Рабочий поток уже взял задачу из рабочей очереди.