Java 8新特性之CompletableFuture:组合式异步编程(2)

public class Shop {
    public double getPrice(String product) throws InterruptedException {
        //查询商品的数据库,或链接其他外部服务获取折扣
        Thread.sleep(1000);
        return new Random().nextDouble() * product.charAt(0) + product.charAt(1);
    }
}

当调用这个方法时,它会阻塞进程,等待事件完成。

将同步方法转换成异步方法

public Future<Double> getPriceAsync(String product){
        //创建CompletableFuture对象
        CompletableFuture<Double> futurePrice = new CompletableFuture<>();

new Thread (()->{
            try {
                //在另一个线程中执行计算
                double price = getPrice(product);
                //需要长时间计算的任务结束并得出结果时,设置future的返回值
                futurePrice.complete(price);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        return futurePrice;
    }

然后可以这样调用:

System.out.println("begin");
        Future<Double> futurePrice = shop.getPriceAsync("ss");
        System.out.println("doSomething");
        System.out.println(futurePrice.get());
        System.out.println("end");

begin
doSomething
171.47509091822835
end

这个例子中,首先会调用接口 立即返回一个Future对象,在这种方式下,在查询价格的同时,还可以处理其他任务。最后所有的工作都已经完成,然后再调用future的get方法。获得Future中封装的值,要么发生阻塞,直到该任务异步任务完成,期望的值能够返回。

错误处理

如果没有意外,这个代码工作的会非常正常。但是如果计算价格的过程中发生了错误,那么get会永久的被阻塞。这时可以使用重载的get方法,让它超过一个时间后就强制返回。应该尽量在代码中使用这种方式来防止程序永久的等待下去。超时会引发TimeoutException。但是这样会导致你无法知道具体什么原因导致Future无法返回,这时需要使用CompletableFUture的completeExceptionally方法将导致CompletableFuture内发生的问题抛出。

public Future<Double> getPriceAsync(String product){
        //创建CompletableFuture对象
        CompletableFuture<Double> futurePrice = new CompletableFuture<>();

new Thread (()->{
            try {
                double price = getPrice(product);
                futurePrice.complete(price);
            } catch (Exception ex) {
                //抛出异常
                futurePrice.completeExceptionally(ex);
            }
        }).start();
        return futurePrice;
    }

调用时:

System.out.println("begin");
        Future<Double> futurePrice = shop.getPriceAsync("ss");
        System.out.println("doSomething");
        try {
            System.out.println(futurePrice.get(1, TimeUnit.SECONDS));
        } catch (TimeoutException e) {
            System.out.print(e);
        }
        System.out.println("end");

设置超时时间,然后会将错误信息打印出来。

工厂方法supplyAsync创建CompletableFuture

使用工厂方法可以一句话来创建getPriceAsync方法

public Future<Double> getPriceAsync(String product) {
        return CompletableFuture.supplyAsync(() -> getPrice(product));
    }

supplyAsync方法接受一个生产者(Supplier)作为参数,返回一个CompletableFuture对象,该对象完成异步执行后悔读取调用生产者方法的返回值。生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行,也可以调用supplyAsync方法的重载版本,传入第二个参数指定不同的线程执行生产者方法。 工厂方法返回的CompletableFuture对象也提供了同样的错误处理机制。

阻塞优化

例如现在有一个商品列表,然后输出一个字符串 商品名,价格 。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/e024cc39655ece152be623006fedb425.html