Я использую Executors для пула потоков и отправляю задачи. Может ли executorService.shutdownNow отключить все задачи, даже если некоторые из них могут быть заблокированы при вызовах ввода-вывода в базу данных или сокет?
Executor shutdownNow с заблокированными задачами?
Ответы (4)
Это зависит от того, хорошо ли написаны ваши задачи!
В документации говорится: "Выключение () позволит выполнить ранее отправленные задачи перед завершением, в то время как метод shutdownNow() предотвращает запуск ожидающих задач и пытается остановить выполнение текущих задач».
Однако Java не убивает потоки «из воздуха». Он пытается их прервать. Хорошая задача выдаст что-то вроде InterruptException
, когда shtudownNow попытается их прервать, и завершится корректно. Вы упоминаете связь через сокеты - большинство методов блокировки приличных клиентов будут генерировать прерванное исключение, если они будут прерваны.
Примером плохой задачи может быть (довольно очевидно) запуск потока с while(true) { readChunk(); if(endOfChunks) { break;} }
. Это не предлагает изящной проверки прерывания! Это старое правило: не использовать циклы while для ожидания, а wait()
использовать syncronized
на объекте-блокировщике, который можно прервать.
Нет, гарантии нет. Если вы видите документ API для ExecutorService#shutdownNow. Он говорит,
Нет никаких гарантий, кроме как сделать все возможное, чтобы остановить обработку активно выполняющихся задач.
Если вы хотите заблокировать выполнение всех задач после запроса на завершение работы, используйте ExecutorService#awaitTermination.
Когда невозможно обрабатывать прерывания (java.io), требуется нестандартная логика завершения работы.
Мое решение для инкапсуляции этой проблемы сочетает в себе примеры «TrackingExecutorService» и «SocketUsingTask» из «Java Concurrency In Practice».
- Определите интерфейс с возможностью отключения
- Расширьте ThreadPoolExecutor для отслеживания запущенных отправленных задач, которые реализуют интерфейс «Shutdownable».
- переопределить останов ThreadPoolExecutor для вызова нестандартной логики завершения работы через интерфейс «Shutdownable»
public interface Shutdownable {
public void shutdown();
}
public class ShutdowningExecutor extends ThreadPoolExecutor{
private final Set runningShutdownables
= Collections.synchronizedSet(new HashSet());
@Override
protected RunnableFuture newTaskFor(final Callable callable){
if (callable instanceof Shutdownable) {
runningShutdownables.add((Shutdownable) callable);
return super.newTaskFor(new Callable(){
@Override
public T call() throws Exception {
T t = callable.call();
runningShutdownables.remove((Shutdownable) callable);
return t;
}
});
} else
return super.newTaskFor(callable);
}
public void shutdownAll() {
for(Shutdownable shutdownable : runningShutdownables) {
shutdownable.shutdown();
}
}
@Override
public List shutdownNow(){
shutdownAll();
return super.shutdownNow();
}
}
public abstract class ShutdownableViaCloseable implements Shutdownable{
private Closeable closeable;
protected synchronized void setCloseable(Closeable c) { closeable = c; }
public synchronized void shutdown() {
try {
if (closeable != null)
closeable.close();
} catch (IOException ignored) { }
}
}
public class MySocketTask extends ShutdownableViaCloseable implements Callable {
public MySocketTask(Socket s) {
setCloseable(s);
//constructor stuff
}
public Void call() {
try (Socket socket = this.socket) {
while(!socket.isClosed) {
//do stuff
}
}
}
}
Проще говоря: на это нельзя полагаться. ExecutorService
просто прерывает а> запущенные задачи; это зависит от выполнения задач, если они действительно отменяют свои усилия. Некоторые операции ввода-вывода могут (и будут) прерываться, особенно java.nio
, но java.io
, скорее всего, не прерываются. См. Что делает java.lang.Thread.interrupt()? для немного больше пояснений.