根据产品名称返回价格(同步和异步实现)

需求

依据指定产品名称返回价格

java 复制代码
public class Shop {
    private final String name;
    private final Random random;
    public Shop(String name) {
        this.name = name;
        random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));
    }

	private double calculatePrice(String product) {
    	// 模拟1秒钟延迟的方法
        int delay = 1000;
        try {
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        // 依据产品的名称,生成一个随机值作为价格
        return random.nextDouble() * product.charAt(0) + product.charAt(1);
    }

    public String getName() {
        return name;
    }
}

同步实现

java 复制代码
// 依据指定产品名称返回价格,同步方法
public double getPrice(String product) {
    return calculatePrice(product);
}

private static void sync() {
    Shop shop = new Shop("BestShop");
    long start = System.nanoTime();
    
    double price = shop.getPrice("product name");
    System.out.println(Thread.currentThread().getName() + " Invocation returned after " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
    
    System.out.printf(Thread.currentThread().getName() + " Price is %.2f%n", price);
    
    doSomethingElse();
    System.out.println(Thread.currentThread().getName() + " Price returned after " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
}
private static void doSomethingElse() {
    System.out.println(Thread.currentThread().getName() + " Do some more tasks, like querying other shops...");
}

// 执行结果
main Invocation returned after 1015 msecs
main Price is 116.32
main Do some more tasks, like querying other shops...
main Price returned after 1033 msecs

异步实现

java 复制代码
// 依据指定产品名称返回价格,将同步方法转换为异步方法
public Future<Double> getPriceAsync(String product) {
    CompletableFuture<Double> futurePrice = new CompletableFuture<>();
    new Thread(() -> {
        double price = calculatePrice(product);
        System.out.println(Thread.currentThread().getName() + " 执行线程任务");
        futurePrice.complete(price);
    }).start();
    return futurePrice;
}

private static void async() {
    Shop shop = new Shop("BestShop");
    long start = System.nanoTime();
    Future<Double> futurePrice = shop.getPriceAsync("product name");
    System.out.println(Thread.currentThread().getName() + " Invocation returned after " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
    doSomethingElse();
    try {
        double price = futurePrice.get();
        System.out.printf(Thread.currentThread().getName() + " Price is %.2f%n", price);
    } catch (ExecutionException | InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println(Thread.currentThread().getName() + " Price returned after " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
}
private static void doSomethingElse() {
    System.out.println(Thread.currentThread().getName() + " Do some more tasks, like querying other shops...");
}

// 执行结果
main Invocation returned after 3 msecs
main Do some more tasks, like querying other shops...
Thread-0 执行线程任务
main Price is 116.32
main Price returned after 1032 msecs

如果价格计算过程中产生了错误会怎样呢?

用于提示错误的异常会被限制在试图计算商品价格的当前线程的范围内,最终会杀死该线程,而这会导致等待get方法返回结果的客户端永久地被阻塞。

如何解决?
方案1:

客户端可以使用重载版本的get方法,它使用一个超时参数来避免发生这样的情况。

java 复制代码
double price = futurePrice.get();  →  double price = futurePrice.get(5, TimeUnit.SECONDS);

这是一种值得推荐的做法,你应该尽量在你的代码中添加超时判断的逻辑,避免发生类似的问题。使用这种方法至少能防止程序永久地等待下去,超时发生时,程序会得到通知发生了TimeoutException。不过,也因为如此,你不会有机会发现计算商品价格的线程内到底发生了什么问题才引发了这样的失效。

方案2:

为了让客户端能了解商店无法提供请求商品价格的原因,你需要使用CompletableFuturecompleteExceptionally方法将导致CompletableFuture内发生问题的异常抛出。

java 复制代码
public Future<Double> getPriceAsync(String product) {
    CompletableFuture<Double> futurePrice = new CompletableFuture<>();
    new Thread(() -> {
        try {
            double price = calculatePrice(product);
            System.out.println(Thread.currentThread().getName() + " 执行线程任务");
            futurePrice.complete(price); // 如果价格计算正常结束,完成Future操作并设置商品价格
        } catch (Exception ex) {
            futurePrice.completeExceptionally(ex); // 否则就抛出导致失败的异常,完成这次Future操作
        }
    }).start();
    return futurePrice;
}

java8的优化写法

java 复制代码
// 依据指定产品名称返回价格,将同步方法转换为异步方法
public Future<Double> getPriceAsync1(String product) {
    CompletableFuture<Double> futurePrice = CompletableFuture
            .supplyAsync(() -> calculatePrice(product))
            .exceptionally(e -> {
                CompletionException ex = new CompletionException(e);
                System.out.println("自定义项目调用异常:" + e.getMessage());
                throw ex;
    });
    return futurePrice;
}

参考

《Java8 实战》第11章 CompletableFuture:组合式异步编程
CompletableFuture

相关推荐
拉不动的猪2 分钟前
刷刷题31(vue实际项目问题)
前端·javascript·面试
zeijiershuai4 分钟前
Ajax-入门、axios请求方式、async、await、Vue生命周期
前端·javascript·ajax
工一木子4 分钟前
【HeadFirst系列之HeadFirstJava】第16天之深入解析 Java 集合与泛型:高效管理数据的终极指南!(含代码实战)
java·集合·泛型
恋猫de小郭6 分钟前
Flutter 小技巧之通过 MediaQuery 优化 App 性能
android·前端·flutter
只会写Bug的程序员15 分钟前
面试之《webpack从输入到输出经历了什么》
前端·面试·webpack
拉不动的猪17 分钟前
刷刷题30(vue3常规面试题)
前端·javascript·面试
码叔义21 分钟前
X509TrustManager信任SSL证书
python·网络协议·ssl
狂炫一碗大米饭27 分钟前
面试小题:写一个函数实现将输入的数组按指定类型过滤
前端·javascript·面试
最胖的小仙女27 分钟前
通过动态获取后端数据判断输入的值打小
开发语言·前端·javascript
阿波拉32 分钟前
AttributeError: module ‘backend_interagg‘ has no attribute ‘FigureCanvas’问题解决
开发语言·python