CompletableFuture, особенности и внутреннее устройство
CompletableFuture — это один из наиболее мощных и гибких инструментов для работы с асинхронными задачами в Java. Впервые представленный в Java 8, он расширяет возможности Future, предоставляя удобные методы для создания, комбинирования и обработки результатов асинхронных вычислений.
CompletableFuture является частью пакета java.util.concurrent и реализует интерфейсы Future и CompletionStage. Это позволяет CompletableFuture комбинировать возможности обоих подходов: он может использоваться как обычный Future, чтобы получать результаты выполнения задач, а также может поддерживать цепочки асинхронных операций через методы CompletionStage.
Future — это интерфейс, который представляет собой результат асинхронного вычисления. Однако он имеет несколько ограничений, таких как отсутствие удобных методов для комбинирования нескольких задач и невозможность завершить Future вручную. Эти ограничения и были решены в CompletableFuture.
Внутреннее устройство
Внутри CompletableFuture основывается на асинхронном выполнении задач, используя ForkJoinPool, который является реализацией Executor и хорошо подходит для задач, разбиваемых на подзадачи. ForkJoinPool оптимизирует распределение задач между потоками, что особенно полезно при выполнении большого количества мелких асинхронных операций.
Когда создается новый экземпляр CompletableFuture, он по умолчанию находится в незавершенном состоянии. Он может быть завершен вручную с помощью методов complete() или автоматически при завершении задачи, связанной с ним. По завершению, CompletableFuture уведомляет все зарегистрированные на него действия (коллбэки), что делает его очень удобным для построения цепочек асинхронных операций.
Особенности CompletableFuture
1. Завершение вручную.
Одной из ключевых особенностей CompletableFuture является возможность завершать его вручную. Это полезно, когда результат выполнения известен заранее, или когда требуется завершить задачу в случае возникновения ошибки.
2. Асинхронное выполнение задач.
CompletableFuture позволяет легко выполнять задачи асинхронно с использованием методов runAsync() и supplyAsync().
3. Построение цепочек асинхронных задач.
Одним из самых мощных аспектов CompletableFuture является возможность построения цепочек асинхронных задач, что позволяет организовать сложные рабочие процессы.
#Java #Training #Medium #CompletableFuture
CompletableFuture — это один из наиболее мощных и гибких инструментов для работы с асинхронными задачами в Java. Впервые представленный в Java 8, он расширяет возможности Future, предоставляя удобные методы для создания, комбинирования и обработки результатов асинхронных вычислений.
CompletableFuture является частью пакета java.util.concurrent и реализует интерфейсы Future и CompletionStage. Это позволяет CompletableFuture комбинировать возможности обоих подходов: он может использоваться как обычный Future, чтобы получать результаты выполнения задач, а также может поддерживать цепочки асинхронных операций через методы CompletionStage.
Future — это интерфейс, который представляет собой результат асинхронного вычисления. Однако он имеет несколько ограничений, таких как отсутствие удобных методов для комбинирования нескольких задач и невозможность завершить Future вручную. Эти ограничения и были решены в CompletableFuture.
Внутреннее устройство
Внутри CompletableFuture основывается на асинхронном выполнении задач, используя ForkJoinPool, который является реализацией Executor и хорошо подходит для задач, разбиваемых на подзадачи. ForkJoinPool оптимизирует распределение задач между потоками, что особенно полезно при выполнении большого количества мелких асинхронных операций.
Когда создается новый экземпляр CompletableFuture, он по умолчанию находится в незавершенном состоянии. Он может быть завершен вручную с помощью методов complete() или автоматически при завершении задачи, связанной с ним. По завершению, CompletableFuture уведомляет все зарегистрированные на него действия (коллбэки), что делает его очень удобным для построения цепочек асинхронных операций.
Особенности CompletableFuture
1. Завершение вручную.
Одной из ключевых особенностей CompletableFuture является возможность завершать его вручную. Это полезно, когда результат выполнения известен заранее, или когда требуется завершить задачу в случае возникновения ошибки.
import java.util.concurrent.CompletableFuture;
public class ManualCompletionExample {
public static void main(String[] args) {
CompletableFuture<String> future = new CompletableFuture<>();
// Завершаем вручную
future.complete("Result");
future.thenAccept(result -> System.out.println("Completed with: " + result));
}
}
В этом примере мы создаем экземпляр CompletableFuture и завершаем его вручную с результатом "Result". Метод thenAccept() регистрирует действие, которое будет выполнено при завершении future.
2. Асинхронное выполнение задач.
CompletableFuture позволяет легко выполнять задачи асинхронно с использованием методов runAsync() и supplyAsync().
import java.util.concurrent.CompletableFuture;
public class AsyncExample {
public static void main(String[] args) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
Thread.sleep(1000);
System.out.println("Asynchronous task");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
future.join(); // Ожидаем завершения
}
}
В этом примере задача выполняется асинхронно, и основной поток ждет ее завершения с помощью join().
3. Построение цепочек асинхронных задач.
Одним из самых мощных аспектов CompletableFuture является возможность построения цепочек асинхронных задач, что позволяет организовать сложные рабочие процессы.
import java.util.concurrent.CompletableFuture;
public class ChainExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(greeting -> greeting + ", World")
.thenAccept(result -> System.out.println(result));
// Ожидаем завершения всех задач
CompletableFuture<Void> allOf = CompletableFuture.allOf();
allOf.join();
}
}
Этот код создает цепочку операций, где одна задача преобразует результат другой.
#Java #Training #Medium #CompletableFuture
Основные методы CompletableFuture и примеры использования
Методы создания
runAsync(Runnable runnable) — выполняет задачу асинхронно и возвращает CompletableFuture<Void>.
supplyAsync(Supplier<U> supplier) — выполняет задачу асинхронно и возвращает CompletableFuture<U>, где U — тип результата.
Методы обработки результата
thenApply(Function<? super T,? extends U> fn) — преобразует результат после завершения задачи.
thenAccept(Consumer<? super T> action) — выполняет действие над результатом.
thenRun(Runnable action) — выполняет действие без использования результата.
Комбинирование нескольких задач
thenCombine(CompletableFuture<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) — комбинирует два CompletableFuture и возвращает новый результат.
thenCompose(Function<? super T,? extends CompletionStage<U>> fn) — создает зависимость между двумя задачами, где вторая зависит от результата первой.
Методы обработки исключений
exceptionally(Function<Throwable,? extends T> fn) — обрабатывает исключение и возвращает альтернативный результат.
handle(BiFunction<? super T, Throwable,? extends U> fn) — обрабатывает результат или исключение.
Примеры использования CompletableFuture в реальных задачах
Параллельное выполнение нескольких задач
#Java #Training #Medium #CompletableFuture
Методы создания
runAsync(Runnable runnable) — выполняет задачу асинхронно и возвращает CompletableFuture<Void>.
supplyAsync(Supplier<U> supplier) — выполняет задачу асинхронно и возвращает CompletableFuture<U>, где U — тип результата.
import java.util.concurrent.CompletableFuture;
public class CreateExample {
public static void main(String[] args) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Running async task");
});
CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
return "Async result";
});
future.join(); // Ожидание завершения
System.out.println(result.join());
}
}
Методы обработки результата
thenApply(Function<? super T,? extends U> fn) — преобразует результат после завершения задачи.
thenAccept(Consumer<? super T> action) — выполняет действие над результатом.
thenRun(Runnable action) — выполняет действие без использования результата.
import java.util.concurrent.CompletableFuture;
public class HandleExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> transformed = future.thenApply(result -> result + ", World");
transformed.thenAccept(result -> System.out.println("Final result: " + result));
}
}
Комбинирование нескольких задач
thenCombine(CompletableFuture<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) — комбинирует два CompletableFuture и возвращает новый результат.
thenCompose(Function<? super T,? extends CompletionStage<U>> fn) — создает зависимость между двумя задачами, где вторая зависит от результата первой.
import java.util.concurrent.CompletableFuture;
public class CombineExample {
public static void main(String[] args) {
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<String> combined = future1.thenCombine(future2, (f1, f2) -> f1 + " " + f2);
combined.thenAccept(result -> System.out.println("Combined result: " + result));
}
}
Методы обработки исключений
exceptionally(Function<Throwable,? extends T> fn) — обрабатывает исключение и возвращает альтернативный результат.
handle(BiFunction<? super T, Throwable,? extends U> fn) — обрабатывает результат или исключение.
import java.util.concurrent.CompletableFuture;
public class ExceptionExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("Something went wrong!");
}
return "Result";
});
future.exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return "Default result";
}).thenAccept(result -> System.out.println("Result: " + result));
}
}
Примеры использования CompletableFuture в реальных задачах
Параллельное выполнение нескольких задач
import java.util.concurrent.CompletableFuture;
public class ParallelTasksExample {
public static void main(String[] args) {
CompletableFuture<Integer> task1 = CompletableFuture.supplyAsync(() -> 2);
CompletableFuture<Integer> task2 = CompletableFuture.supplyAsync(() -> 3);
CompletableFuture<Integer> result = task1.thenCombine(task2, (a, b) -> a * b);
result.thenAccept(res -> System.out.println("Multiplication result: " + res));
}
}
#Java #Training #Medium #CompletableFuture
Выполнение задачи с таймаутом
Если задача выполняется слишком долго, можно установить таймаут, после которого будет возвращён результат по умолчанию или выброшено исключение.
Цепочка зависимых асинхронных операций
Часто требуется выполнить несколько асинхронных операций последовательно, передавая результат одной задачи в другую.
Асинхронная обработка коллекций данных
Представим, что у нас есть список элементов, и для каждого элемента нужно выполнить асинхронную операцию, например, запрос в базу данных или внешний API.
#Java #Training #Medium #CompletableFuture
Если задача выполняется слишком долго, можно установить таймаут, после которого будет возвращён результат по умолчанию или выброшено исключение.
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class TimeoutExample {
public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000); // Имитация длительной задачи
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Task completed";
});
try {
String result = future.get(1, TimeUnit.SECONDS); // Устанавливаем таймаут в 1 секунду
System.out.println(result);
} catch (TimeoutException e) {
System.out.println("Timeout occurred");
} catch (Exception e) {
e.printStackTrace();
}
}
}
В этом примере задача длится 2 секунды, но установленный таймаут — 1 секунда. В результате будет выведено сообщение о таймауте.
Цепочка зависимых асинхронных операций
Часто требуется выполнить несколько асинхронных операций последовательно, передавая результат одной задачи в другую.
import java.util.concurrent.CompletableFuture;
public class ChainedAsyncExample {
public static void main(String[] args) {
CompletableFuture.supplyAsync(() -> "Hello")
.thenApplyAsync(greeting -> greeting + ", World")
.thenApplyAsync(message -> message + "!")
.thenAcceptAsync(System.out::println);
CompletableFuture<Void> allOf = CompletableFuture.allOf(); // Для ожидания завершения всех задач
allOf.join(); // Ожидаем завершения
}
}
В этом примере цепочка асинхронных операций последовательно преобразует строку, добавляя к ней различные части и выводя окончательный результат на консоль.
Асинхронная обработка коллекций данных
Представим, что у нас есть список элементов, и для каждого элемента нужно выполнить асинхронную операцию, например, запрос в базу данных или внешний API.
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
public class AsyncProcessingExample {
public static void main(String[] args) {
List<String> data = Arrays.asList("One", "Two", "Three");
List<CompletableFuture<String>> futures = data.stream()
.map(item -> CompletableFuture.supplyAsync(() -> processItem(item)))
.collect(Collectors.toList());
List<String> results = futures.stream()
.map(CompletableFuture::join) // Ожидаем завершения всех задач
.collect(Collectors.toList());
results.forEach(System.out::println);
}
private static String processItem(String item) {
try {
Thread.sleep(1000); // Имитация асинхронной операции
} catch (InterruptedException e) {
e.printStackTrace();
}
return "Processed: " + item;
}
}
Этот пример демонстрирует асинхронную обработку списка элементов, где каждая операция выполняется параллельно, а результаты собираются и выводятся после завершения всех задач.
#Java #Training #Medium #CompletableFuture