Перенаправление исключения из одного потока в другой

#java #multithreading #exception #concurrency

#Ява #многопоточность #исключение #совпадение

Вопрос:

У меня есть сценарий, в котором я хочу, чтобы один поток выполнял некоторые циклические операции, а второй (основной) поток выполнял какую-то другую циклическую работу, пока первый поток все еще выполняет свою работу.

Моя идея состояла в том, чтобы использовать CountDownLatch и ждать, пока он не будет завершен в главном потоке:

 public void process() {   CountDownLatch countDownLatch = new CountDownLatch(10_000);  Futurelt;?gt; future = Executors.newSingleThreadExecutor().submit(() -gt; {  for (int i = 0; i lt; 10_000; i  ) {  // do some stuff  countDownLatch.countDown();  }  });   try {  while (!countDownLatch.await(5, SECONDS)) {  // do some other stuff...  }  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  }  }  

Проблема в том, что иногда исключение может быть выдано в первом (будущем) потоке, и в таком случае также не имеет смысла продолжать выполнение кода в основном потоке.

Я думал о том, чтобы присвоить ссылку на такое исключение (созданное из первого потока) полю volatile и выполнить проверку на нуль этого поля в цикле потоков main, чтобы увидеть, следует ли ему продолжать цикл:

 private volatile Exception innerException;  public void process() {   CountDownLatch countDownLatch = new CountDownLatch(10_000);  Futurelt;?gt; future = Executors.newSingleThreadExecutor().submit(() -gt; {  try {  for (int i = 0; i lt; 10_000; i  ) {  // do some stuff  countDownLatch.countDown();  }  } catch (Exception e) {  this.innerException = e;  throw e;  }  });   try {  while (!countDownLatch.await(1, SECONDS)) {  // do some other stuff... but it doesn't make sense to continue  // if 'future' has thrown an exception, so let's rethrow it:  if (innerException != null) {  throw innerException;  }  }  } catch (InterruptedException e) {  Thread.currentThread().interrupt();  } catch (Exception e) {  log.error("Something bad happened in the 'future'! : ", e);  } }   

Мне интересно, хорошая ли это (безопасная?) идея или, может быть, есть какие-то лучшие способы решения такого рода проблем?

Ценю любую помощь в этом вопросе, спасибо!

Ответ №1:

Вы можете синхронизировать по завершении будущего с помощью future.get. Если выполняемый/вызываемый объект создает исключение, future.get создаст исключение ExecutionException. Вы можете полностью избавиться от обратного отсчета.

Комментарии:

1. Да, это звучит как хорошая идея, поэтому я предполагаю, что вы думали о чем-то подобном в основном цикле: while (!isFinished(future)) { // do some other stuff... } и проверьте, готово ли будущее: private static boolean isFinished(Futurelt;?gt; future) { try { future.get(1, SECONDS); return true; } catch (TimeoutException e) { return false; } catch (ExecutionException e) { throw new RuntimeException(e); } } Спасибо за предложение!

2. Существует метод «isDone» для будущего, который можно использовать, чтобы проверить, готово ли будущее. Я не уверен, что проверка isDone в каком-то цикле-лучший подход; это будет зависеть от ситуации.

3. Или используйте CompletableFuturelt;?gt; future = CompletableFuture.runAsync(() -gt; { /* your operation */ }, yourExecutor); , тогда вы можете использовать не только isDone() для проверки завершения в целом, но и, например if(future.isCompletedExceptionally()) future.join(); , для повторного броска.