Оглавление:
Асинхронность и реактивность в Java: CompletableFuture, Flow и Virtual Threads
В современном Java-разработке есть три основных подхода к асинхронности и параллельности:
- CompletableFuture — для одиночных асинхронных задач.
- Flow / Reactive Streams — для потоков данных с контролем скорости (backpressure).
- Virtual Threads / Loom — для масштабируемой параллельности без блокировок.
Образное понимание
Flow — это «река данных с контролем течения».
Virtual Threads — это «миллионы работников», которые готовы обрабатывать данные в своей скорости, но не умеют замедлять реку.
CompletableFuture — это «единичный груз», который доставляется асинхронно.
Сравнение подходов
| Механизм | Сильная сторона | Когда использовать |
|---|---|---|
| CompletableFuture (Java 8) | Простая асинхронность для одиночных задач, цепочки действий | API-запросы, DB, файловые операции |
| Flow / Reactive Streams (Java 9) | Поток данных с контролем скорости (backpressure), пайплайны обработки событий | Стриминг, брокеры сообщений, WebFlux, event-driven системы |
| Virtual Threads / Loom (Java 21) | Массовая параллельность без блокировок, линейный код | Web-серверы, API, масштабируемые сервисы |
Примеры кода
1. CompletableFuture — одиночная асинхронная задача
import java.util.concurrent.*;
public class CompletableFutureExample {
public static void main(String[] args) throws Exception {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
sleep(500);
return "Hello from CompletableFuture";
});
future.thenAccept(System.out::println);
Thread.sleep(1000);
}
private static void sleep(long ms) {
try { Thread.sleep(ms); } catch (InterruptedException e) {}
}
}
2. Flow — поток данных с backpressure
import java.util.concurrent.Flow.*;
import java.util.concurrent.SubmissionPublisher;
public class FlowExample {
public static void main(String[] args) throws Exception {
SubmissionPublisher<Integer> publisher = new SubmissionPublisher<>();
Subscriber<Integer> subscriber = new Subscriber<>() {
private Subscription subscription;
@Override
public void onSubscribe(Subscription subscription) {
this.subscription = subscription;
subscription.request(5);
}
@Override
public void onNext(Integer item) {
System.out.println("Received: " + item);
sleep(200);
}
@Override
public void onError(Throwable throwable) { throwable.printStackTrace(); }
@Override
public void onComplete() { System.out.println("Done!"); }
};
publisher.subscribe(subscriber);
for (int i = 1; i <= 10; i++) publisher.submit(i);
publisher.close();
Thread.sleep(3000);
}
private static void sleep(long ms) {
try { Thread.sleep(ms); } catch (InterruptedException e) {}
}
}
3. Virtual Threads — миллионы параллельных задач (Java 21+)
public class VirtualThreadsExample {
public static void main(String[] args) throws Exception {
for (int i = 1; i <= 10; i++) {
Thread.startVirtualThread(() -> {
System.out.println("Hello from virtual thread " + Thread.currentThread().getName());
sleep(200);
});
}
Thread.sleep(1000);
}
private static void sleep(long ms) {
try { Thread.sleep(ms); } catch (InterruptedException e) {}
}
}
Flow (река данных)
[Publisher] --> [Subscriber] --> [Subscriber]
^ контроль скорости (backpressure)
Virtual Threads (работники)
[Task1] [Task2] [Task3] ... [TaskN]
каждый работает в своей скорости, никто не тормозит
CompletableFuture — одиночный груз
Async Task ---> Result
\
---> thenAccept / thenApply
Когда коллбеки нужны
CompletableFuture -> коллбеки почти всегда
Flow -> коллбеки через onNext/onComplete
Virtual Threads -> коллбеки почти не нужны
Вывод
Каждая модель асинхронности имеет свою силу и применяется для разных бизнес-задач:
- CompletableFuture — для единичных задач, где важна простота.
- Flow — для потоков данных с контролем скорости, где важна надежность и backpressure.
- Virtual Threads — для масштабируемых серверов, где важна читаемость и параллельность без коллбеков.
Галерея
Мой канал в социальных сетях
Полезные статьи:
Признаки легаси-проекта: как распознать старый корабль Легаси — это не просто старый код. Это живой организм, который пережил десятки изменений, смену команд, устаревшие технологии и множество временн...
Сегодня мы разберем основные элементы Java: Statement (инструкции) Блоки кода Создадим простейшую программу Hello World! Разберем каждое слово в коде Научимся писать комментарии, которые не исполняют...
Предисловие Мир программного обеспечения уже давно перестал быть спокойным океаном: сегодня это бурная экосистема, где каждая миллисекунда отклика приложения может стоить компании клиентов, репутации ...
Новые статьи:
Схема - Java Memory Model - Heap / Non-Heap / Stack Heap (память для объектов) Создаёт объекты через new. Young Generation: Eden + Survivor. Old Generation: объекты, пережившие несколько сборок G...
Признаки легаси-проекта: как распознать старый корабль Легаси — это не просто старый код. Это живой организм, который пережил десятки изменений, смену команд, устаревшие технологии и множество временн...
В современном Java-разработке есть три основных подхода к асинхронности и параллельности: CompletableFuture — для одиночных асинхронных задач. Flow / Reactive Streams — для потоков данных с контролем...