2009-09-02 22 views
38

Hola, estoy escribiendo una aplicación de red, en la que leo paquetes de algún formato binario personalizado. Y estoy comenzando un hilo de fondo para esperar los datos entrantes. El problema es que el compilador no me permite poner excepciones de código arrojado (marcado) en run(). Dice:Cómo lanzar una excepción marcada de un hilo de Java?

run() in (...).Listener cannot implement run() in java.lang.Runnable; overridden method does not throw java.io.IOException

Quiero excepción para matar al hilo, y que sea atrapado en algún lugar en el hilo de los padres. ¿Es posible lograr esto o tengo que manejar cada excepción dentro de el hilo?

+1

Tenga una mirada en respuesta siguiente: [Cómo capturar una excepción de un hilo] [1] [1]: http://stackoverflow.com/questions/ 6546193/cómo-coger-una-excepción-de-un-hilo –

Respuesta

36

Advertencia: esto puede no satisfacer sus necesidades si tiene que utilizar el mecanismo de excepción.

Si lo entiendo correctamente, en realidad no necesita que se compruebe la excepción (ha aceptado la respuesta que sugiere una excepción sin marcar), ¿sería más apropiado un patrón de oyente simple?

El oyente podría vivir en el subproceso principal, y cuando haya capturado la excepción marcada en el subproceso secundario, simplemente podría notificar al oyente.

Esto significa que tiene una forma de exponer que esto sucederá (a través de métodos públicos), y podrá pasar más información de la permitida por una excepción. Pero sí significa que habrá un acoplamiento (aunque suelto) entre el padre y el hilo hijo. Dependería de su situación específica si esto tendría un beneficio sobre envolver la excepción marcada con una desactivada.

Aquí está un ejemplo sencillo (algo de código tomado de otra respuesta):

public class ThingRunnable implements Runnable { 
    private SomeListenerType listener; 
    // assign listener somewhere 

    public void run() { 
     try { 
      while(iHaveMorePackets()) { 
       doStuffWithPacket(); 
      } 
     } catch(Exception e) { 
      listener.notifyThatDarnedExceptionHappened(...); 
     } 
    } 
} 

El acoplamiento viene de un objeto en el hilo padre tener que ser de tipo SomeListenerType.

+0

Buena idea. Y sí, tienes razón, no me importa si la excepción está marcada o no, solo quería una manera simple de sacar una excepción. – mik01aj

+0

¿El código del oyente aún no se ejecuta realmente en el hilo hijo? Entiendo que ahora tiene gran parte del alcance en el hilo padre ... pero aún se ejecuta en el hilo hijo. – Jared

+0

Se ejecutará en el hilo hijo, pero coincide con lo que se ha pedido, creo. – Grundlefleck

3

Si realmente no puede hacer nada útil cuando se produce la excepción, puede envolver la excepción marcada en una RuntimeException.

try { 
    // stuff 
} catch (CheckedException yourCheckedException) { 
    throw new RuntimeException("Something to explain what is happening", yourCheckedException); 
} 
+4

Siempre agregue una descripción al lanzar una excepción, incluso si solo se trata de envolver. throw new RuntimeException ("Excepción de ajuste para permitir que suba hasta el receptor en foo.bar.Main()", e); Los que se llamen a las 3 a.m. cuando se rompa el código, lo apreciarán cuando miren fijamente a la pila. –

1

Si el código de su hilo arroja una RuntimeExpection, no necesita agregar run() throw Exception.

Pero utilizar esta solución sólo cuando sea necesario ya que esto puede ser una mala pratice: http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

Cualquier RuntimeException o excepción no se controla puede ayudarle. Tal vez necesite crear su propia RuntimeException

+2

Tenga en cuenta que este enfoque ocultará la excepción del subproceso primario. – erickson

3

el hilo no puede lanzar la excepción a ningún otro hilo (ni al hilo principal). y no puede hacer que el método heredado run() arroje ninguna excepción comprobada ya que solo puede arrojar menos del código heredado, no más. al final del método run()

public class ThingRunnable implements Runnable { 
    public void run() { 
    try { 
     while(iHaveMorePackets()) { 
     doStuffWithPacket() 
     } 
    } catch(Exception e) { 
     System.out.println("Runnable terminating with exception" + e); 
    } 
    } 
} 

La excepción se puede romper automáticamente de su bucle, y:

+0

Buen punto sobre no poder "lanzar" directamente la excepción al hilo principal. – erickson

0

En el supuesto de que el código está en una especie de bucle, escribiría , el hilo se detendrá.

+0

Pequeño punto: su ejemplo tiene una interfaz con implementación en él, probablemente debería ser "clase pública ThingRunnable implementa Runnable". – Grundlefleck

+0

Gracias, Grundlefleck, tienes toda la razón :-) Va a mostrar lo que sucede cuando respondes preguntas antes de haber tomado suficiente café en el día. Actualicé el texto para decir 'clase' en su lugar. – AlBlue

7

Lo que hago es atrapar la excepción en el hilo y almacenarla como una variable miembro de Runnable. Esta excepción se expone luego a través de un getter en Runnable. Luego escaneo todos los hilos del padre para ver si hubo excepciones y tomo las medidas adecuadas.

+1

¿Puedo preguntar (en caso de que mi respuesta sea incorrecta), por qué almacenar una variable como getter y escanear en lugar de utilizar un mecanismo de escucha? – Grundlefleck

+0

Pregunta justa. Cualquiera de los enfoques funciona. Creo que voy a intentar tu sugerencia la próxima vez, y ver si prefiero eso. –

+0

Consulte mi comentario sobre la respuesta de Grundlefleck: creo que esta solución realmente recibe el tratamiento de excepciones en el hilo principal, mientras que la solución de Grundlefleck no. (Grundlefleck soluciona los problemas de alcance, pero este soluciona los problemas que están realmente relacionados con el contexto del subproceso.) – Jared

45

Para poder enviar la excepción a la rosca de los padres, puede poner el hilo en un fondo Callable (que permita realizar el lanzamiento también la inspección de excepciones) que luego se pasa al método de some Executorsubmit. El método de envío devolverá un Future que luego puede usar para obtener la excepción (su método get arrojará un ExecutionException que contiene la excepción original).

+3

Esto se ve demasiado complicado para mí. Pero gracias de todos modos :) – mik01aj

31

Esta respuesta se basa en Esko Luontola uno, pero proporciona un ejemplo de trabajo.

A diferencia del método run() de la interfaz Runnable el método call() de Callable permite lanzar algunas excepciones. He aquí un ejemplo de implementación:

public class MyTask implements Callable<Integer> { 

    private int numerator; 
    private int denominator; 

    public MyTask(int n, int d) { 
     this.numerator = n; 
     this.denominator = d; 
    } 

    @Override 
    // The call method may throw an exception 
    public Integer call() throws Exception { 
     Thread.sleep(1000); 
     if (denominator == 0) { 
      throw new Exception("cannot devide by zero"); 
     } else { 
      return numerator/denominator; 
     } 
    } 

} 

Ejecutor proporciona un mecanismo para ejecutar un rescatable dentro de un hilo y para manejar cualquier tipo de excepciones:

public class Main { 

    public static void main(String[] args) { 

     // Build a task and an executor 
     MyTask task = new MyTask(2, 0); 
     ExecutorService threadExecutor = Executors.newSingleThreadExecutor(); 

     try { 
      // Start task on another thread 
      Future<Integer> futureResult = threadExecutor.submit(task); 

      // While task is running you can do asynchronous operations 
      System.out.println("Something that doesn't need the tasks result"); 

      // Now wait until the result is available 
      int result = futureResult.get(); 
      System.out.println("The result is " + result); 
     } catch (ExecutionException e) { 
      // Handle the exception thrown by the child thread 
      if (e.getMessage().contains("cannot devide by zero")) 
       System.out.println("error in child thread caused by zero division"); 
     } catch (InterruptedException e) { 
      // This exception is thrown if the child thread is interrupted. 
      e.printStackTrace(); 
     } 
    } 
} 
+6

Esto resuelve el problema de las excepciones no detectadas, pero podría presentar problemas propios. La llamada a 'get()' se bloqueará hasta que regrese la tarea invocable. Esto significa que ya no obtendrá ningún paralelismo del hilo de fondo. Peor aún, si la tarea es continua/de larga ejecución (como esperar paquetes de red en un bucle), 'Callable' _ nunca_ regresará y su hilo principal quedará bloqueado permanentemente. No quiero decir que no haya aplicaciones donde este patrón tenga sentido (estoy seguro de que hay muchas), solo que debe tener cuidado. – theisenp

+0

Buen punto. En un ejemplo real, no llamarías 'get' justo después de' submit'. –

+0

En un ejemplo real, puede iniciar N operaciones en segundo plano en paralelo y esperar los resultados de M operaciones en segundo plano donde M <= N para tomar una decisión. En ese caso, llamarías para obtenerlo justo después de enviarlo. – Ameliorator

-1

Envolviendo su excepción dentro de un RuntimeException parece hacer el truco .

someMethod() throws IOException 
{ 
    try 
    { 
     new Thread(() -> 
     { 
      try 
      { 
       throw new IOException("a checked exception thrown from within a running thread"); 
      } 
      catch(IOException ex) 
      { 
       throw new RuntimeException("a wrapper exception", ex); // wrap the checked exception inside an unchecked exception and throw it 
      } 
     }).start(); 
    } 
    catch(RuntimeException ex) // catch the wrapped exception sent from within the thread 
    { 
     if(ex.getCause() instanceof IOException) 
      throw ex.getCause; // unwrap the checked exception using getCause method and use it however you need 
     else 
      throw ex; 
    } 
} 
+0

No funciona – Richard

Cuestiones relacionadas