В программировании часто возникает необходимость выполнять длительные операции, такие как сетевые запросы, чтение и запись файлов или взаимодействие с базами данных. В традиционном синхронном подходе, когда задачи выполняются последовательно, основной поток блокируется на каждом запросе, ожидая его завершения. Это приводит к значительному увеличению общего времени выполнения, особенно если необходимо обработать несколько запросов. Например, если три запроса занимают по 2 секунды каждый, общее время выполнения составит 6 секунд. Это может негативно сказаться на производительности и отзывчивости приложения.
Асинхронность
Асинхронность предлагает эффективный подход к решению этой проблемы. В асинхронном программировании задачи могут выполняться параллельно, что позволяет основному потоку не ждать завершения каждой операции. Вместо этого он продолжает выполнять другие задачи, пока запросы обрабатываются в фоновом режиме. Как только все запросы будут завершены, вы можете обработать результаты.
Если вы запускаете три запроса одновременно, и каждый из них занимает 2 секунды, общее время выполнения будет равно времени самого длительного запроса — 2 секунды. Это значительно сокращает время ожидания и улучшает отзывчивость приложения. Таким образом, асинхронный подход позволяет более эффективно использовать ресурсы и повышает производительность, особенно в условиях, когда необходимо обрабатывать множество длительных операций.
Класс CompletableFuture
CompletableFuture
— это класс в Java, который предоставляет мощные инструменты для работы с асинхронными задачами и упрощает управление их выполнением. Он позволяет создавать цепочки асинхронных операций, обрабатывать результаты и ошибки, а также комбинировать несколько задач, что делает код более читаемым и удобным для сопровождения
Метод runAsync()
в классе CompletableFuture
позволяет запускать асинхронные задачи, которые не возвращают результат. Он принимает Runnable
в качестве аргумента и выполняет его в отдельном потоке, что позволяет основному потоку продолжать выполнение других операций
class Example {
public static void main(String[] args) {
// Если нам нужно просто асинхронно выполнить задачу и не нужно ничего возвращать
// В виде лямбды передаём объект Runnable
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
// Задержка имитирует длительно выполняющуюся задачу
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
System.out.println("Run in separate thread");
});
// Ожидаем окончания работы задачи
future1.get();
}
}
Метод supplyAsync()
в классе CompletableFuture
используется для запуска асинхронных задач, которые возвращают результат. Он принимает Supplier<T>
в качестве аргумента, который выполняется в отдельном потоке и возвращает значение типа T
.
После завершения задачи, результат можно получить и обработать
class Example {
public static void main(String[] args) {
// Асинхронный запуск задачи и возврат результата работы
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
// Задержка имитирует длительно выполняющуюся задачу
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
return "Result of async computation";
});
// Ожидаем окончания работы задачи и получаем результат
String result = future2.get(); // "Result of async computation"
}
}
Если нам нужно объединить результаты двух асинхронных задач, мы можем использовать метод thenCombine()
класса CompletableFuture
. Этот метод позволяет дождаться завершения обеих задач и применить функцию, которая объединяет их результаты в одно значение.
class Example {
public static void main(String[] args) {
// Комбинирование двух CompletableFuture
// Если одна задача не зависит от другой
// Нужно выполнить две задачи независимо одна от другой и выполнить третью,
// когда предыдущее будут завершены
// Задачи выполняются независимо друг от друга
System.out.println("Retrieving weight");
CompletableFuture<Integer> futureWeight = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
throw new IllegalStateException(e);
}
return 100;
});
System.out.println("Retrieving volume");
CompletableFuture<Integer> futureVolume = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
throw new IllegalStateException(e);
}
return 2;
});
// выполняется после завершения первых двух
System.out.println("Calculate density");
CompletableFuture<Integer> futureDensity = futureWeight.thenCombine(futureVolume, (weight, volume) -> {
Integer density = weight / volume;
return density;
// Обработка исключений
// Если при работе задач возникли исключения
// их можно обработать в методе exceptionally
}).exceptionally(ex -> {
System.out.println("Oops! We have an exception - " + ex.getMessage());
return null;
});
}
}

Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.