Como pegar corretamente exceções de tempo de execução de Executores?
-
18-09-2019 - |
Pergunta
dizer que tenho o seguinte código:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(myRunnable);
Agora, se myRunnable
lança uma RuntimeExcpetion
, como posso pegá-lo? Uma forma seria para abastecer minha própria implementação ThreadFactory
para newSingleThreadExecutor()
e definir uncaughtExceptionHandler
s personalizados para os Thread
s que vêm de fora. Outra forma seria para embrulhar myRunnable
a um (anônimo) Runnable
local que contém um -bloco try-catch. Talvez existam outras soluções semelhantes também. Mas ... de alguma forma este se sente sujo, eu sinto que ele não deve ser tão complicado. Existe uma solução limpa?
Solução
A solução limpa é usar ExecutorService.submit()
vez de execute()
. Isso você um Future
que você pode usar para recuperar o resultado ou exceção da tarefa retornos:
ExecutorService executor = Executors.newSingleThreadExecutor();
Runnable task = new Runnable() {
public void run() {
throw new RuntimeException("foo");
}
};
Future<?> future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
Exception rootException = e.getCause();
}
Outras dicas
Decore o executável em outro executável que captura as exceções de tempo de execução e manipula-los:
public class REHandler implements Runnable {
Runnable delegate;
public REHandler (Runnable delegate) {
this.delegate = delegate;
}
public void run () {
try {
delegate.run ();
} catch (RuntimeException e) {
... your fancy error handling here ...
}
}
}
executor.execute(new REHandler (myRunnable));
Por que não chamar ExecutorService#submit()
, obter o Future
para trás e, em seguida, lidar com possíveis exceções si mesmo ao chamar Future#get()
?
skaffman é correto que usando submit
é a abordagem mais limpa. Uma abordagem alternativa é a subclasse ThreadPoolExecutor
e afterExecute(Runnable, Throwable)
override. Se você seguir esta abordagem certifique-se de chamada execute(Runnable)
em vez de submit(Runnable)
ou afterExecute
não será invocado.
Pela descrição API:
Método invocado após a conclusão da execução do dado Runnable. Esta método é invocado pelo segmento que executado a tarefa. Se não-nulo, o Throwable é o uncaught
RuntimeException
ouError
que causou execução para terminar abruptamente.Nota: Quando as ações são colocados tarefas (tais como FutureTask) tanto explicitamente ou através de métodos tais como apresentar, essas tarefas objetos capturar e manter exceções computacionais, e para que eles não causam abrupta de terminação, e o interno as exceções são não passado para este método .
uma tarefa (Callable
ou Runnable
) submetidos a ThreadPoolExecutors
será convertido a um FuturnTask
, contém um suporte chamado callable
é igual a tarefa de enviar. FuturnTask tem seu próprio método run
como segue. Todos exceção ou throwable Throwed em c.call()
será catched e colocar em um suporte chamado outcome
. Ao chamar método get
de FuturnTask, outcome
será Throwed
FuturnTask.run partir do código fonte Jdk1.8
public void run() {
...
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// save ex into `outcome` prop
setException(ex);
}
if (ran)
set(result);
}
}
...
}
Se você deseja capturar a exceção:
- 1. de skaffman resposta ??ol>
- 2. sobrescrever `afterExecute` quando você novo a ThreadPoolExecutor
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
Throwable cause = null;
if (t == null && r instanceof Future) {
try {
((Future<?>) r).get();
} catch (InterruptedException | ExecutionException e) {
cause = e;
}
} else if (t != null) {
cause = t;
}
if (cause != null) {
// log error
}
}