Como capturar uma exceção de um segmento

Eu tenho class principal Java, na class, eu inicio um novo segmento, no principal, aguarda até que o segmento morra. Em algum momento, eu jogo uma exceção de tempo de execução do encadeamento, mas não consigo capturar a exceção lançada do encadeamento na class principal.

Aqui está o código:

public class Test extends Thread { public static void main(String[] args) throws InterruptedException { Test t = new Test(); try { t.start(); t.join(); } catch(RuntimeException e) { System.out.println("** RuntimeException from main"); } System.out.println("Main stoped"); } @Override public void run() { try { while(true) { System.out.println("** Started"); sleep(2000); throw new RuntimeException("exception from thread"); } } catch (RuntimeException e) { System.out.println("** RuntimeException from thread"); throw e; } catch (InterruptedException e) { } } } 

Alguém sabe por quê?

Use um Thread.UncaughtExceptionHandler .

 Thread.UncaughtExceptionHandler h = new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread th, Throwable ex) { System.out.println("Uncaught exception: " + ex); } }; Thread t = new Thread() { public void run() { System.out.println("Sleeping ..."); try { Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("Interrupted."); } System.out.println("Throwing exception ..."); throw new RuntimeException(); } }; t.setUncaughtExceptionHandler(h); t.start(); 

Isso explica a transição de estado dos encadeamentos, dependendo se as exceções ocorreram ou não:

Threads e tratamento de exceções

Isso é porque as exceções são locais para um segmento, e seu segmento principal não vê realmente o método de run . Sugiro que você leia mais sobre como funciona o threading, mas para resumir rapidamente: sua chamada para start inicia um thread diferente, totalmente não relacionado ao seu thread principal. O chamado para join simplesmente espera que seja feito. Uma exceção que é lançada em um thread e nunca capturada a encerra, é por isso que join retorna em seu thread principal, mas a exceção em si é perdida.

Se você quiser estar ciente dessas exceções não detectadas, você pode tentar isto:

 Thread.setDefaultExceptionHandler(new UncaughtExceptionHandler() { public void unchaughtException(Thread t, Throwable e) { System.out.println("Caught " + e); } }); 

Mais informações sobre o tratamento de exceções não identificadas podem ser encontradas aqui .

Provavelmente;

  • você não precisa passar a exceção de um segmento para outro.
  • Se você quiser manipular uma exceção, basta fazer isso no encadeamento que a lançou.
  • seu thread principal não precisa esperar do thread em segundo plano neste exemplo, o que na verdade significa que você não precisa de um thread em segundo plano.

No entanto, vamos supor que você precisa manipular uma exceção de um outro segmento filho. Eu usaria um ExecutorService assim:

 ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(new Callable() { @Override public Void call() throws Exception { System.out.println("** Started"); Thread.sleep(2000); throw new IllegalStateException("exception from thread"); } }); try { future.get(); // raises ExecutionException for any uncaught exception in child } catch (ExecutionException e) { System.out.println("** RuntimeException from thread "); e.getCause().printStackTrace(System.out); } executor.shutdown(); System.out.println("** Main stopped"); 

impressões

 ** Started ** RuntimeException from thread java.lang.IllegalStateException: exception from thread at Main$1.call(Main.java:11) at Main$1.call(Main.java:6) at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303) at java.util.concurrent.FutureTask.run(FutureTask.java:138) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662) ** Main stopped 

Por favor, dê uma olhada em Thread.UncaughtExceptionHandler

Melhor maneira (alternativa) é usar Callable e Future para obter o mesmo resultado …

Use Callable invés de Thread, então você pode chamar Future#get() que lança qualquer exceção que o Callable jogou.

Atualmente você está pegando apenas RuntimeException , uma subclass de Exception . Mas seu aplicativo pode lançar outras subclasss de Exceção . Captura Exception genérica além de RuntimeException

Como muitas das coisas foram alteradas no threading, use a API Java avançada.

Prefira a API java.util.concurrent avançada para multiencadeamento como ExecutorService ou ThreadPoolExecutor .

Você pode personalizar seu ThreadPoolExecutor para manipular exceções.

Exemplo da página de documentação do oracle:

Sobrepor

 protected void afterExecute(Runnable r, Throwable t) 

Método invocado após a conclusão da execução do dado Runnable. Este método é chamado pelo encadeamento que executou a tarefa. Se não for nulo, o Throwable é o RuntimeException ou Erro não detectado que causou a finalização abrupta da execução.

Exemplo de código:

 class ExtendedExecutor extends ThreadPoolExecutor { // ... protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t == null && r instanceof Future< ?>) { try { Object result = ((Future< ?>) r).get(); } catch (CancellationException ce) { t = ce; } catch (ExecutionException ee) { t = ee.getCause(); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); // ignore/reset } } if (t != null) System.out.println(t); } } 

Uso:

 ExtendedExecutor service = new ExtendedExecutor(); 

Eu adicionei um construtor no topo do código acima como:

  public ExtendedExecutor() { super(1,5,60,TimeUnit.SECONDS,new ArrayBlockingQueue(100)); } 

Você pode alterar esse construtor para atender às suas necessidades no número de threads.

 ExtendedExecutor service = new ExtendedExecutor(); service.submit(); 

Você brincou com setDefaultUncaughtExceptionHandler () e os methods similares da class Thread? Da API: “Ao definir o manipulador de exceções não detectado padrão, um aplicativo pode alterar a maneira como as exceções não identificadas são tratadas (como o registro em um dispositivo ou arquivo específico) para os segmentos que já aceitariam o comportamento” padrão ” sistema fornecido. ”

Você pode encontrar a resposta para o seu problema aí … boa sorte! 🙂

Eu enfrentei o mesmo problema … pouco trabalho (apenas para implementação não objects anônimos) … podemos declarar o object de exceção de nível de class como null … então inicializá-lo dentro do bloco catch para o método run … se houver foi erro no método run, esta variável não será nula .. podemos então ter a verificação nula para esta variável em particular e se não for nula então houve exceção dentro da execução do thread.

 class TestClass implements Runnable{ private Exception ex; @Override public void run() { try{ //business code }catch(Exception e){ ex=e; } } public void checkForException() throws Exception { if (ex!= null) { throw ex; } } } 

chame checkForException () depois de join ()

É quase sempre errado extender o Thread . Eu não posso afirmar isso com força suficiente.

Regra Multithreading # 1: Estender Thread está errado. *

Se você implementar o Runnable , verá o comportamento esperado.

 public class Test implements Runnable { public static void main(String[] args) { Test t = new Test(); try { new Thread(t).start(); } catch (RuntimeException e) { System.out.println("** RuntimeException from main"); } System.out.println("Main stoped"); } @Override public void run() { try { while (true) { System.out.println("** Started"); Thread.sleep(2000); throw new RuntimeException("exception from thread"); } } catch (RuntimeException e) { System.out.println("** RuntimeException from thread"); throw e; } catch (InterruptedException e) { } } } 

produz;

 Main stoped ** Started ** RuntimeException from threadException in thread "Thread-0" java.lang.RuntimeException: exception from thread at Test.run(Test.java:23) at java.lang.Thread.run(Thread.java:619) 

* a menos que você queira mudar a maneira como seu aplicativo usa threads, o que em 99,9% dos casos você não usa. Se você acha que está no 0,1% dos casos, consulte a regra 1.

Se você implementar Thread.UncaughtExceptionHandler na class que inicia os Threads, você pode definir e, em seguida, retroceder a exceção:

 public final class ThreadStarter implements Thread.UncaughtExceptionHandler{ private volatile Throwable initException; public void doSomeInit(){ Thread t = new Thread(){ @Override public void run() { throw new RuntimeException("UNCAUGHT"); } }; t.setUncaughtExceptionHandler(this); t.start(); t.join(); if (initException != null){ throw new RuntimeException(initException); } } @Override public void uncaughtException(Thread t, Throwable e) { initException = e; } } 

Qual causa a seguinte saída:

 Exception in thread "main" java.lang.RuntimeException: java.lang.RuntimeException: UNCAUGHT at com.gs.gss.ccsp.enrichments.ThreadStarter.doSomeInit(ThreadStarter.java:24) at com.gs.gss.ccsp.enrichments.ThreadStarter.main(ThreadStarter.java:38) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) Caused by: java.lang.RuntimeException: UNCAUGHT at com.gs.gss.ccsp.enrichments.ThreadStarter$1.run(ThreadStarter.java:15) 

Manipulação de exceção em Thread: Por padrão, o método run () não lança nenhuma exceção, portanto, todas as exceções verificadas dentro do método de execução devem ser capturadas e tratadas somente e, para exceções de tempo de execução, podemos usar UncaughtExceptionHandler. UncaughtExceptionHandler é uma interface fornecida pelo Java para manipular exceções em um método de execução de Thread. Assim, podemos implementar essa interface e retornar nossa class de implementação de volta ao object Thread usando o método setUncaughtExceptionHandler (). Mas esse manipulador deve ser definido antes de chamar start () no piso.

Se não definirmos uncaughtExceptionHandler, o Threads ThreadGroup atua como um manipulador.

  public class FirstThread extends Thread { int count = 0; @Override public void run() { while (true) { System.out.println("FirstThread doing something urgent, count : " + (count++)); throw new RuntimeException(); } } public static void main(String[] args) { FirstThread t1 = new FirstThread(); t1.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { System.out.printf("Exception thrown by %s with id : %d", t.getName(), t.getId()); System.out.println("\n"+e.getClass()); } }); t1.start(); } } 

Uma boa explicação dada em http://coder2design.com/thread-creation/#exceptions

Minha solução com o RxJava:

 @Test(expectedExceptions = TestException.class) public void testGetNonexistentEntry() throws Exception { // using this to work around the limitation where the errors in onError (in subscribe method) // cannot be thrown out to the main thread AtomicReference ex = new AtomicReference<>(); URI id = getRandomUri(); canonicalMedia.setId(id); client.get(id.toString()) .subscribe( m -> fail("Should not be successful"), e -> ex.set(new TestException())); for(int i = 0; i < 5; ++i) { if(ex.get() != null) throw ex.get(); else Thread.sleep(1000); } Assert.fail("Cannot find the exception to throw."); } 

Você não pode fazer isso, já que não faz sentido. Se você não chamou t.join() , o thread principal pode estar em qualquer lugar no código quando o thread t gera uma exceção.