#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();
, для повторного броска.