2009-09-02 3 views
38

Эй, я пишу сетевое приложение, в котором я читаю пакеты какого-то пользовательского двоичного формата. И я начинаю фоновый поток, чтобы ждать входящих данных. Проблема заключается в том, что компилятор не позволяет мне помещать исключения кода (проверенные) в run(). В нем написано:Как выбросить проверенное исключение из потока java?

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

Я хочу исключение, чтобы убить поток и позволить ему быть пойманным где-то в родительском потоке. Можно ли достичь или я должен обрабатывать все исключения внутри нить?

+1

Посмотрите на следующий ответ: [Как поймать исключение из потока] [1] [1]: http://stackoverflow.com/questions/ 6546193/how-to-catch-the-exception-from-a-thread –

ответ

36

Предостережение: это может не соответствовать вашим потребностям, если вам нужно использовать механизм исключения.

Если я правильно вас понимаю, вам действительно не нужно исключение, которое нужно проверить (вы приняли ответ, предлагающий исключение без отметки), так что бы более простой пример прослушивания был более уместным?

Слушатель может жить в родительском потоке, и когда вы поймали проверенное исключение в дочернем потоке, вы можете просто уведомить слушателя.

Это означает, что у вас есть способ разоблачения того, что это произойдет (посредством общедоступных методов) и сможет передавать больше информации, чем это позволит исключение. Но это означает, что между родительским и дочерним потоком будет связь (пусть и свободная). В вашей конкретной ситуации будет зависеть от того, будет ли это иметь преимущество перед переносом проверенного исключения с неконтролируемой.

Вот простой пример (часть кода заимствована из другого ответа):

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

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

Соединение происходит из объекта в родительской нити, имеющей быть типа SomeListenerType.

+0

Хорошая идея. И да, вы правы, меня не волнует, проверено ли исключение или нет, я просто хотел, чтобы простой способ исключить исключение. – mik01aj

+0

Действительно ли код прослушивателя еще не выполняется в дочернем потоке? Я понимаю, что теперь он имеет большую часть области в родительском потоке ... но он все еще работает в дочернем потоке. – Jared

+0

Он будет работать в дочернем потоке, но он соответствует тому, что было предложено, я думаю. – Grundlefleck

3

Если вы действительно ничего не можете сделать полезными при возникновении исключения, вы можете обернуть исключенное исключение в RuntimeException.

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

Всегда добавляйте описание при бросании исключения, даже если оно просто нужно обернуть. throw new RuntimeException («Обтекание исключений, чтобы оно могло пузыриться до ловушки в foo.bar.Main()», e); Те, кого звонили в 3 часа ночи, когда разрывается код, оценят его, когда они будут смотреть на стек. –

1

Если код вашей нити вызывает RuntimeExpection, вам не нужно добавлять исключение Run() throw Exception.

Но использовать это решение только в случае необходимости, поскольку это может быть плохой pratice: http://java.sun.com/docs/books/tutorial/essential/exceptions/runtime.html

Любой RuntimeException или незарегистрированное Исключение может помочь вам. Возможно, вам нужно будет создать собственное RuntimeException

+2

Обратите внимание, что этот подход скроет исключение из родительского потока. – erickson

3

нить не может выбросить исключение ни в какую другую нить (ни в основной поток). и вы не можете заставить унаследованный метод run() выкидывать любые проверенные исключения, так как вы можете бросить меньше, чем унаследованный код, а не больше.

+0

Хороший вопрос о невозможности прямого «выбросить» исключение из родительского потока. – erickson

0

В предположении, что ваш код находится в какой-то цикл, вы бы написать:

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

Исключение автоматически сломает вас из цикла, и в конце пробега() метод , поток остановится.

+0

Маленькая точка: ваш пример имеет интерфейс с реализацией в нем, вероятно, должен быть «публичным классом ThingRunnable реализует Runnable». – Grundlefleck

+0

Спасибо, Grundlefleck, вы абсолютно правы :-) Идет, чтобы показать, что происходит, когда вы отвечаете на вопросы, прежде чем у вас будет достаточно кофе в день. Я обновил текст, чтобы сказать «класс». – AlBlue

7

Я делаю это, чтобы поймать исключение в потоке и сохранить его как переменную-член Runnable. Это исключение затем открывается через геттер на Runnable. Затем я просматриваю все потоки от родителя, чтобы узнать, есть ли исключения, и предпримите соответствующие действия.

+1

Могу ли я спросить (если мой ответ неверен), зачем хранить переменную в качестве геттера и сканировать ее, а не использовать механизм прослушивания? – Grundlefleck

+0

Ярмарка вопрос. Любой подход работает. Думаю, я попробую ваше предложение в следующий раз, и посмотрю, хочу ли я это. –

+0

Посмотрите мой комментарий на ответ Grundlefleck - я считаю, что это решение действительно возвращает обработку исключений в родительский поток, тогда как решение Grundlefleck этого не делает. (Grundlefleck исправляет проблемы с областью, но это устраняет проблемы, которые действительно связаны с контекстом потоков.) – Jared

45

Чтобы иметь возможность отправить исключение в родительском потоке, вы можете поместить свой фоновый поток в Callable (это позволяет бросать также проверяемые исключения), которые вы затем перейти к submit методу some Executor. Метод submit возвращает Future, который затем можно использовать для получения исключения (его метод get будет вызывать ExecutionException, который содержит исходное исключение).

+3

Это выглядит слишком сложно для меня. Но спасибо вам все равно :) – mik01aj

31

Этот ответ основан на Esko Luontola, но он служит рабочим примером.

В отличие от метода run() интерфейса Runnable метод call() Callable позволяет вызывать некоторые исключения. Вот пример реализации:

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; 
     } 
    } 

} 

Исполнитель предоставляет механизм для запуска отзывных внутри нити и для обработки любых исключений:

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

Это решает проблему неперехваченных исключений, но может создавать проблемы самостоятельно. Вызов 'get()' будет блокироваться, пока вызываемая задача не вернется. Это означает, что вы больше не получаете параллелизма из фонового потока. Хуже того, если задача непрерывна/долго работает (например, ожидание сетевых пакетов в цикле), 'Callable' будет _never_ возвращаться, и ваш основной поток будет постоянно заблокирован. Я не имею в виду, что нет приложений, где этот шаблон имеет смысл (я уверен, что их много), просто нужно быть осторожным. – theisenp

+0

Хорошая точка. В реальном примере вы не будете называть 'get' прямо после' submit'. –

+0

В реальном примере вы можете параллельно инициализировать N фоновых операций и ждать результатов фоновых операций M, где M <= N, чтобы принять решение. В этом случае вы можете позвонить сразу после отправки. – Ameliorator

-1

Обертывания вашего исключения Внутри RuntimeException, кажется, сделать трюк ,

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

Не работает – Richard

Смежные вопросы