Java Language
CompletableFuture
Поиск…
Вступление
CompletableFuture - это класс, добавленный в Java SE 8, который реализует интерфейс Future от Java SE 5. Помимо поддержки интерфейса Future, он добавляет множество методов, которые позволяют асинхронный обратный вызов, когда будущее будет завершено.
Преобразование метода блокировки в асинхронный
Следующий метод займет секунду или два в зависимости от вашего подключения, чтобы получить веб-страницу и подсчитать длину текста. Какими бы ни были потоковые вызовы, они будут блокироваться в течение этого периода времени. Также он вызывает исключение, которое полезно позже.
public static long blockingGetWebPageLength(String urlString) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(new URL(urlString).openConnection().getInputStream()))) {
StringBuilder sb = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
return sb.toString().length();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
Это преобразует его в метод, который немедленно возвращается, перемещая вызов метода блокировки в другой поток. По умолчанию метод supplyAsync будет запускать поставщика в общий пул. Для метода блокировки это, вероятно, не очень хороший выбор, поскольку вы могли бы исчерпать потоки в этом пуле, поэтому я добавил дополнительный параметр сервиса.
static private ExecutorService service = Executors.newCachedThreadPool();
static public CompletableFuture<Long> asyncGetWebPageLength(String url) {
return CompletableFuture.supplyAsync(() -> blockingGetWebPageLength(url), service);
}
Чтобы использовать функцию в асинхронном режиме, следует использовать методы, которые принимают ламду, которую вызывают с результатом поставщика, когда она завершается, например thenAccept. Также важно использовать метод исключения или обработки для регистрации любых исключений, которые могли произойти.
public static void main(String[] args) {
asyncGetWebPageLength("https://stackoverflow.com/")
.thenAccept(l -> {
System.out.println("Stack Overflow returned " + l);
})
.exceptionally((Throwable throwable) -> {
Logger.getLogger("myclass").log(Level.SEVERE, "", throwable);
return null;
});
}
Простой пример CompletingFuture
В приведенном ниже примере, calculateShippingPrice
метод вычисляет стоимость доставки, которая занимает некоторое время обработки. Например, в реальном мире это может быть обращение к другому серверу, который возвращает цену, основанную на весе продукта и способе доставки.
Путем моделирования этого методом async через CompletableFuture
мы можем продолжить разную работу в методе (т.е. рассчитать затраты на упаковку).
public static void main(String[] args) {
int price = 15; // Let's keep it simple and work with whole number prices here
int weightInGrams = 900;
calculateShippingPrice(weightInGrams) // Here, we get the future
.thenAccept(shippingPrice -> { // And then immediately work on it!
// This fluent style is very useful for keeping it concise
System.out.println("Your total price is: " + (price + shippingPrice));
});
System.out.println("Please stand by. We are calculating your total price.");
}
public static CompletableFuture<Integer> calculateShippingPrice(int weightInGrams) {
return CompletableFuture.supplyAsync(() -> {
// supplyAsync is a factory method that turns a given
// Supplier<U> into a CompletableFuture<U>
// Let's just say each 200 grams is a new dollar on your shipping costs
int shippingCosts = weightInGrams / 200;
try {
Thread.sleep(2000L); // Now let's simulate some waiting time...
} catch(InterruptedException e) { /* We can safely ignore that */ }
return shippingCosts; // And send the costs back!
});
}