TypechoJoeTheme

香草物语

统计
登录
用户名
密码
/
注册
用户名
邮箱
输入密码
确认密码

使用CompletableFuture<T>实现异步接口

Laughing博主
2022-10-23
/
0 评论
/
527 阅读
/
478 个字
/
百度已收录
10/23
本文最后更新于2024年03月15日,已超过44天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

【同步API】
同步API是我们传统方法,调用方法在被调用方法运行的过程中会一直等待,直到被调用方法返回。此时的调用过程,是阻塞式的调用。

【异步API】
与同步API相反,异步API会直接返回,或者至少在被调用方计算完成之前,将它剩余的计算任务交给另外一个线程去做,该线程和调用方是异步的,是非阻塞式调用。

来看一下【同步API】与【异步API】的区别


public class Shop {

    public double getPrice(String product) {
        return calculatePrice(product);
    }

    public Future<Double> getPriceAsync(String product) {
        CompletableFuture<Double> futurePrice = new CompletableFuture<>();
        new Thread(() -> {
            double price = calculatePrice(product);
            futurePrice.complete(price);
        }).start();
        return futurePrice;
    }

    private double calculatePrice(String product) {
        delay();
        return new Random().nextDouble() * product.charAt(0) * product.charAt(1);
    }

    public static void delay() {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException interruptedException) {
            throw new RuntimeException(interruptedException);
        }
    }

}

在上面的代码中,getPrice()是一个同步方法,其内部调用的calculatePrice()用于模拟价格计算,计算过程中,暂停线程1s用于模拟阻塞。

然后我们调用getPrice(),查看程序执行时间。


public class Main {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {

        Shop shop = new Shop();
        long startTime = System.currentTimeMillis();
        double doubleFuture = shop.getPrice("apple");
        doSomeThingElse();
        long endTime = System.currentTimeMillis();
        System.out.println("总耗时:" + (endTime - startTime) + "ms");
    }

    private static void doSomeThingElse(){
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

此时查看控制台,我们可以看到共执行了3011ms,也就是说,此时整个程序是串行的。

此时我们再来看看另一个方法getPriceAsync(),此方法不再返回double类型,而是一个Future<Double>Future<T>接口标识一个异步计算的结果,泛型代表返回值。Future的计算结果,通过get()方法获取。

getPriceAsync()方法中,我们创建了一个代表异步计算的CompletableFuture<T>,它在计算完成后,会包含计算的结果。接着创建一个线程去执行实际的操作,不必等到线程结束,直接返回一个Future<Double>实例。当线程计算结束后,调用CompletableFuture<T>complete()方法,结束CompletableFuture<T>的运行。

然后我们调用getPriceAsync()方法,查看程序执行时间。

public class Main {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {

        Shop shop = new Shop();
        long startTime = System.currentTimeMillis();
        Future<Double> doubleFuture = shop.getPriceAsync("apple");
        doSomeThingElse();
        doubleFuture.get(2, TimeUnit.SECONDS);
        long endTime = System.currentTimeMillis();
        System.out.println("总耗时:" + (endTime - startTime) + "ms");
    }

    private static void doSomeThingElse(){
        try {
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

此时查看控制台,可以看到总体耗时2108ms,也就是说我们主程序跟getPriceAsync()方法此时是并行的了。

【改进的异步方法】

在前面的getPriceAsync()方法中,我们创建了一个线程创建CompletableFuture<T>对象,实际上,还有一种更简便的方式,即使用工厂方法CompletableFuture.supplyAsync创建CompletableFuture<T>对象。

改进后的代码如下所示


    public Future<Double> getPriceAsync(String product) {
//        CompletableFuture<Double> futurePrice = new CompletableFuture<>();
//        new Thread(() -> {
//            double price = calculatePrice(product);
//            futurePrice.complete(price);
//        }).start();
//        return futurePrice;
        return CompletableFuture.supplyAsync(()-> calculatePrice(product));
    }
Java
朗读
赞(1)
赞赏
感谢您的支持,我会继续努力哒!
版权属于:

香草物语

本文链接:

https://www.xiangcaowuyu.net/java/implementing-asynchronous-interfaces-using-completeablefuture.html(转载时请注明本文出处及文章链接)

评论 (0)