Асинхронные HTTP запросы в Java: аналог async/await в C#
Быстрый ответ
В Java аналогом async/await
из C# служит класс CompletableFuture
. Он обеспечивает реализацию асинхронности и бесблокирующих операций. Возьмем такой пример:
CompletableFuture.supplyAsync(() -> "LongRunningMethod()")
.thenApplyAsync(result -> "Process(" + result + ")")
.thenAccept(System.out::println)
.exceptionally(e -> { System.err.println(e); return null; });
Здесь мы выполняем асинхронную операцию, затем обрабатываем результат, и, в конце, выводим его с помощью System.out.println
. Этот процесс напоминает применение async/await
в C#. В случае ошибки используем метод exceptionally
.
Почему нужно использовать асинхронное программирование?
Использование асинхронности в Java (и не только) предоставляет нам ряд преимуществ. Она позволяет проводить бесблокирующие операции, улучшает масштабируемость, упрощает сложные операции и обеспечивает эффективную обработку исключений.
Выясним, что такое CompletableFuture
Мастерство создания цепочек
В Java также можно создавать цепочки асинхронных задач с использованием методов thenApply
, thenCompose
, thenCombine
:
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
Thread.sleep(5000); // Эмуляция долгосрочной операции
return "Результат операции";
});
completableFuture
.thenApply(result -> result + " – обработан")
.thenAccept(System.out::println); // Печатаем результат
Управление потоками исполнения
С помощью ExecutorService
мы можем эффективно управлять потоками при работе с CompletableFuture
:
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture.supplyAsync(() -> "Задача с отдельным исполнителем", executor)
.thenAcceptAsync(System.out::println, executor);
executor.shutdown();
Синхронное управление задачами
Метод CompletableFuture.allOf
позволяет обработать сразу несколько задач:
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(future1, future2, future3);
combinedFuture.get();
Альтернативы и добавочные инструменты
Несмотря на то, что CompletableFuture
довольно функционален, он не полностью воспроизводит async/await
. В этом контексте полезно рассмотреть:
- RxJava: предлагает операторы для асинхронного и событийного программирования.
- Quasar: волокна, используемые как более легковесная альтернатива потокам.
- AsyncHttpClient и AsyncCompletionHandler: предназначены для асинхронных HTTP-операций.
- JavaFlow и библиотека Continuations: имитируют синтаксис
async/await
.
Неизведанные территории
RxJava
В реактивном программировании с RxJava используется такая конструкция:
Observable<String> observable = ...; // Формируем Observable
observable
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.single())
.subscribe(System.out::println);
Волокна от Quasar
Волокна Quasar позволяют достичь параллелизма с меньшими затратами:
Fiber<Void> fiber = new Fiber<>(() -> {
System.out.println("Внутри волокна");
return null;
}).start();
Асинхронный ввод/вывод
В Java асинхронный I/O можно реализовать с помощью AsynchronousFileChannel
и других подобных классов:
Path file = Paths.get("file.txt");
AsynchronousFileChannel channel = AsynchronousFileChannel.open(file, READ, WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("Чтение завершено!");
}
public void failed(Throwable exc, ByteBuffer attachment) {
// Обрабатываем ошибку
}
});
Визуализация
Входение в структуру аналога async/await
из Java через метафору оркестра:
C# async/await:
🎼: "Вступление!" 🕒 -> 🎻(async) + 🎷(async) + 🥁(await) -> 🎼: "Гармония!"
Java с CompletableFuture:
🎼: "Создаём мелодию!" 🕒 -> 🎻(supplyAsync) + 🎷(thenCompose) -> 🎼: "Заключение!"
Примеры из практики
Асинхронные HTTP-запросы с использованием AsyncHttpClient
AsyncHttpClient client = Dsl.asyncHttpClient();
client.prepareGet("https://example.com")
.execute()
.toCompletableFuture()
.thenApply(Response::getResponseBody)
.thenAccept(System.out::println)
.join();
client.close();
Лямбда-выражения
Лямбды позволяют упростить код:
CompletableFuture.supplyAsync(() -> {
return "Такой вот лямбда-выражение.";
}).thenAccept(joke -> System.out.println("Шутка: " + joke));
Управление состояниями в асинхронном режиме
CompletableFuture
поддерживает переходы между состояниями.
GitHub-репозитории с конкретными примерами
Изучайте применение асинхронного программирования на основе реальных примеров из GitHub:
- reactive-programming-in-java-8-with-rxjava: реактивное программирование с использованием RxJava.
- async-http-client: асинхронные HTTP- и WebSocket-клиенты.
- quasar: легковесные потоки и акторы.
Полезные материалы
- CompletableFuture (Java Platform SE 8 ) — официальная документация по CompletableFuture в Java.
- Асинхронное программирование в Java: понятия и принципы — обстоятельное руководство по теме асинхронности.
- Асинхронное программирование в Java 8: как использовать CompletableFuture — практическое введение в работу с CompletableFuture.
- Введение в CompletableFuture в Java 8 – YouTube — лекция Венката Субраманиама о CompletableFuture.
- Знакомимся с Reactive Streams API в Java — вводный материал по асинхронной обработке данных с применением Reactive Streams API.
- Различия между CompletableFuture и CompletionStage — рассматриваются отличия и специфичные аспекты применения CompletableFuture и CompletionStage.