Estou tentando usar a ThreadPoolExecutor
classe Java para executar um grande número de tarefas pesadas com um número fixo de threads. Cada uma das tarefas possui muitos locais nos quais pode falhar devido a exceções.
Subclassifiquei ThreadPoolExecutor
e substituí o afterExecute
método que deve fornecer quaisquer exceções não detectadas encontradas durante a execução de uma tarefa. No entanto, não consigo fazê-lo funcionar.
Por exemplo:
public class ThreadPoolErrors extends ThreadPoolExecutor {
public ThreadPoolErrors() {
super( 1, // core threads
1, // max threads
1, // timeout
TimeUnit.MINUTES, // timeout units
new LinkedBlockingQueue<Runnable>() // work queue
);
}
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if(t != null) {
System.out.println("Got an error: " + t);
} else {
System.out.println("Everything's fine--situation normal!");
}
}
public static void main( String [] args) {
ThreadPoolErrors threadPool = new ThreadPoolErrors();
threadPool.submit(
new Runnable() {
public void run() {
throw new RuntimeException("Ouch! Got an error.");
}
}
);
threadPool.shutdown();
}
}
A saída deste programa é "Está tudo bem - situação normal!" mesmo que o único Runnable enviado ao conjunto de encadeamentos emita uma exceção. Alguma pista do que está acontecendo aqui?
Obrigado!
Respostas:
Dos documentos :
Quando você envia um Runnable, ele será envolvido em um futuro.
Seu afterExecute deve ser algo como isto:
fonte
future.isDone()
? ComoafterExecute
é executado após aRunnable
conclusão, presumofuture.isDone()
sempre retornostrue
.AVISO : Observe que esta solução bloqueará o segmento de chamada.
Se você deseja processar exceções geradas pela tarefa, geralmente é melhor usá-lo
Callable
do queRunnable
.Callable.call()
tem permissão para lançar exceções verificadas e elas são propagadas de volta para o segmento de chamada:Se
Callable.call()
lança uma exceção, ela será envolta emExecutionException
e lançada porFuture.get()
.É provável que seja muito preferível à subclasse
ThreadPoolExecutor
. Também oferece a oportunidade de reenviar a tarefa se a exceção for recuperável.fonte
future.get()
ou sua versão sobrecarregada for chamada.A explicação para esse comportamento está correta no javadoc para afterExecute :
fonte
Eu consegui contornar o problema, executando o executável fornecido enviado ao executor.
fonte
whenComplete()
método deCompletableFuture
.Estou usando a
VerboseRunnable
classe do jcabi-log , que engole todas as exceções e as registra. Muito conveniente, por exemplo:fonte
Outra solução seria usar o ManagedTask e o ManagedTaskListener .
Você precisa de um Callable ou Runnable que implemente a interface ManagedTask .
O método
getManagedTaskListener
retorna a instância que você deseja.E você implementa no ManagedTaskListener o
taskDone
método:Mais detalhes sobre o ciclo de vida da tarefa gerenciada e o ouvinte .
fonte
Isso funciona
Ele criará um Executor com um único thread, que pode executar muitas tarefas; e aguardará a atual terminar a execução para começar com a próxima
Em caso de erro ou exceção do uncaugth, o uncaughtExceptionHandler o capturará
fonte
Se você deseja monitorar a execução da tarefa, pode girar 1 ou 2 threads (talvez mais dependendo da carga) e usá-los para executar tarefas de um wrapper ExecutionCompletionService.
fonte
Se você
ExecutorService
vem de uma fonte externa (ou seja, não é possível subclassificarThreadPoolExecutor
e substituirafterExecute()
), você pode usar um proxy dinâmico para obter o comportamento desejado:fonte
Isso é por causa
AbstractExecutorService :: submit
está envolvendo seurunnable
emRunnableFuture
(nadaFutureTask
) como abaixoEm seguida
execute
, passará paraWorker
eWorker.run()
chamará o abaixo.fonte
Isso é semelhante à solução da mmm, mas um pouco mais compreensível. Faça com que suas tarefas estendam uma classe abstrata que envolva o método run ().
fonte
Em vez de subclassificar ThreadPoolExecutor, eu forneceria uma instância ThreadFactory que cria novos Threads e fornece a eles um UncaughtExceptionHandler
fonte