1. Будущее
JDK 5 представил режим будущего. Интерфейс Future представляет собой реализацию многопоточного режима Future в Java.В пакете java.util.concurrent можно выполнять асинхронные вычисления.
Шаблон Future — это шаблон проектирования, обычно используемый в многопоточном проектировании. Режим Future можно понимать так: у меня есть задача, и я отправляю ее в Future, а Future выполняет эту задачу за меня. А пока я могу делать все, что захочу. Через некоторое время я смогу получить результаты из Будущего.
Интерфейс Future очень прост, всего пять методов.
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Методы интерфейса Future описываются следующим образом:
- boolean cancel (boolean mayInterruptIfRunning) Отменяет выполнение задачи. Параметр указывает, следует ли немедленно прервать выполнение задачи или дождаться завершения задачи.
- boolean isCancelled () Была ли задача отменена, если задача отменена до ее нормального завершения, она вернет true
- boolean isDone () Завершена ли задача. Следует отметить, что если задача завершена нормально, аварийно или отменена, она вернет true
- V get() выбрасывает InterruptedException, ExecutionException Дождитесь окончания выполнения задачи, после чего получите результат типа V. Поток InterruptedException является прерванным исключением, исключение выполнения задачи ExecutionException, если задача отменена, также будет выбрано CancellationException
- V get (длительный тайм-аут, модуль TimeUnit) генерирует InterruptedException, ExecutionException, TimeoutException То же, что и функция get выше, с добавлением установки времени тайм-аута. Параметр timeout указывает время ожидания, а uint указывает единицу времени, которая определена в классе перечисления TimeUnit. Если время вычисления истекло, будет выброшено исключение TimeoutException.
В обычных обстоятельствах мы будем использовать Callable и Future вместе, выполнять Callable через метод отправки ExecutorService и возвращать Future.
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(() -> { //Lambda 是一个 callable, 提交后便立即执行,这里返回的是 FutureTask 实例
System.out.println("running task");
Thread.sleep(10000);
return "return task";
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("do something else"); //前面的的 Callable 在其他线程中运行着,可以做一些其他的事情
try {
System.out.println(future.get()); //等待 future 的执行结果,执行完毕之后打印出来
} catch (InterruptedException e) {
} catch (ExecutionException e) {
} finally {
executor.shutdown();
}
По сравнению с future.get() на самом деле более рекомендуется использовать метод get (длинный тайм-аут, блок TimeUnit).Установка тайм-аута может предотвратить бесконечное ожидание программой результата будущего.
2. Введение в CompletableFuture
2.1 Недостатки будущего шаблона
-
Хотя Future может выполнить требование получения результата асинхронного выполнения, он не предоставляет механизма уведомления, и мы не можем знать, когда Future завершится.
-
Либо используйте блокировку, либо дождитесь результата, возвращаемого future в future.get(), который затем становится синхронной операцией. Или используйте isDone() для опроса, чтобы определить, завершено ли Future, что потребует ресурсов ЦП.
2.2 Введение в CompletableFuture
Netty и Guava соответственно расширяют интерфейс Java Future для облегчения асинхронного программирования.
Новый класс CompletableFuture Java 8 поглощает все возможности ListenableFuture и SettableFuture в Google Guava, а также предоставляет другие мощные функции, позволяющие Java иметь полную неблокирующую модель программирования: Future, Promise и Callback (до Java8 только Future без Перезвони).
CompletableFuture может выполнять обратный вызов в потоке, отличном от задачи, или использовать обратный вызов как синхронную функцию, которая продолжает выполняться в том же потоке, что и задача. Это позволяет избежать самой большой проблемы с традиционными обратными вызовами, а именно возможности разделить поток управления на разные обработчики событий.
CompletableFuture компенсирует недостатки шаблона Future. После завершения асинхронной задачи вам не нужно ждать, когда вам нужно продолжить работу с ее результатом. Результат предыдущей асинхронной обработки может быть передан другому потоку асинхронной обработки событий для обработки непосредственно через thenAccept, thenApply, thenCompose и т. д.
3. Функции CompletableFuture
3.1 Статический фабричный метод CompletableFuture
имя метода | описывать |
---|---|
runAsync(Runnable runnable) | Выполнение асинхронного кода с использованием ForkJoinPool.commonPool() в качестве пула потоков. |
runAsync(Runnable runnable, Executor executor) | Выполнение асинхронного кода с использованием указанного пула потоков. |
supplyAsync(Supplier<U> supplier) | Используйте ForkJoinPool.commonPool() в качестве пула потоков для выполнения асинхронного кода, а асинхронные операции имеют возвращаемые значения. |
supplyAsync(Supplier<U> supplier, Executor executor) | Используйте указанный пул потоков для выполнения асинхронного кода, и асинхронная операция имеет возвращаемое значение. |
Разница между методами runAsync и SupplyAsync заключается в том, что CompletableFuture, возвращаемый runAsync, не имеет возвращаемого значения.
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Hello");
});
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("CompletableFuture");
CompletableFuture, возвращаемый SupplyAsync, определяется возвращаемым значением. Следующий код выводит возвращаемое значение future.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("CompletableFuture");
3.2 Completable
имя метода | описывать |
---|---|
complete(T t) | Завершите асинхронное выполнение и верните результат будущего |
completeExceptionally(Throwable ex) | Аномальное завершение асинхронного выполнения |
Когда future.get() ожидает результата выполнения, программа всегда блокируется, и если в это время вызывается complete(T t), она будет выполнена немедленно.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.complete("World");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Результаты:
World
Вы можете видеть, что будущий вызов complete(T t) будет выполнен немедленно. Но метод complete(T t) может быть вызван только один раз, и последующие повторные вызовы будут недействительны.
Если future уже был выполнен и может вернуть результат, вызов complete(T t) в это время будет недействителен.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
future.complete("World");
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Результаты:
Hello
Использование completeExceptionally(Throwable ex) вызывает исключение вместо успешного результата.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello");
future.completeExceptionally(new Exception());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Результаты:
java.util.concurrent.ExecutionException: java.lang.Exception
...