Java Language
CompletableFuture
Buscar..
Introducción
CompletableFuture es una clase agregada a Java SE 8 que implementa la interfaz Future de Java SE 5. Además de admitir la interfaz Future, agrega muchos métodos que permiten la devolución de llamadas asíncronas cuando se completa el futuro.
Convertir el método de bloqueo a asíncrono.
El siguiente método tomará uno o dos segundos, dependiendo de su conexión para recuperar una página web y contar la longitud del texto. Cualquiera que sea el hilo que llame, se bloqueará durante ese período de tiempo. También vuelve a lanzar una excepción que es útil más adelante.
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);
}
}
Esto lo convierte en un método que regresará inmediatamente moviendo la llamada al método de bloqueo a otro hilo. De forma predeterminada, el método supplyAsync ejecutará el proveedor en el grupo común. Para un método de bloqueo, probablemente esta no sea una buena opción ya que uno podría agotar los subprocesos en ese grupo, por lo que agregué el parámetro de servicio opcional.
static private ExecutorService service = Executors.newCachedThreadPool();
static public CompletableFuture<Long> asyncGetWebPageLength(String url) {
return CompletableFuture.supplyAsync(() -> blockingGetWebPageLength(url), service);
}
Para usar la función de forma asíncrona, se debe usar uno de los métodos que aceptan una lamda para que se llame con el resultado del proveedor cuando se complete, como por ejemplo, Aceptar. También es importante usar excepcionalmente o manejar un método para registrar cualquier excepción que pueda haber ocurrido.
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;
});
}
Ejemplo simple de CompletableFuture
En el ejemplo siguiente, el calculateShippingPrice
método calcula el coste de envío, lo que lleva algún tiempo de procesamiento. En un ejemplo del mundo real, esto sería, por ejemplo, ponerse en contacto con otro servidor que devuelve el precio según el peso del producto y el método de envío.
Al modelar esto de forma asíncrona a través de CompletableFuture
, podemos continuar con diferentes trabajos en el método (es decir, calcular los costos de empaque).
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!
});
}