На сколько плохо использовать join внутри Future?
Есть ли принципиальная разница между этими примерами кода?
someCompletionStage.thenApply(string -> doSomething(string));
CompletableFuture.supplyAsync(() -> {
String string = someCompletionStage.join();
doSomething(string);
});
Понимаю, что первое идиоматичнее и легче читается, но есть ли разница в поведении и эффективности?
Ответы (1 шт):
Автор решения: Alexander Pavlov
→ Ссылка
join внутри Future нужно использовать очень аккуратно, вот пример дэдлока
import java.util.concurrent.*;
public class Program
{
public static void main(String[] args) throws Exception {
var p = Executors.newSingleThreadExecutor();
var someCompletionStage = CompletableFuture
.supplyAsync(()->{
try { Thread.sleep(1000); } catch (Exception ex) {}
return "hi!";
}, p)
.thenApplyAsync(s -> {
try { Thread.sleep(1000); } catch (Exception ex) {}
return s;
}, p);
CompletableFuture.runAsync(() -> {
String string = someCompletionStage.join();
System.out.println(string);
}, p)
.join();
System.out.println("bye!");
p.shutdown();
}
}
Происходит это потому, что "третий" CompletableFuture.runAsync успевает вклиниться между "первым" supplyAsync и "вторым" thenApplyAsync, блокирует поток джойном, поэтому вторая часть уже никогда не выполнится.
С цепочкой вызовов, естественно, дэдлок не случится.