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

需求

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

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

相关推荐
listhi5203 分钟前
Map对象在JavaScript循环中的使用
开发语言·前端·javascript
安卓开发者10 分钟前
鸿蒙Next Web组件生命周期详解:从加载到销毁的全流程掌控
前端
卓码软件测评2 小时前
第三方软件测试机构【性能测试工具用LoadRunner还是JMeter?】
java·功能测试·测试工具·jmeter·性能优化
我叫汪枫3 小时前
前端物理引擎库推荐 - 让你的网页动起来!
前端
xchenhao3 小时前
SciKit-Learn 全面分析分类任务 breast_cancer 数据集
python·机器学习·分类·数据集·scikit-learn·svm
Lionel_SSL6 小时前
《深入理解Java虚拟机》第三章读书笔记:垃圾回收机制与内存管理
java·开发语言·jvm
记得开心一点嘛6 小时前
手搓Springboot
java·spring boot·spring
老华带你飞6 小时前
租房平台|租房管理平台小程序系统|基于java的租房系统 设计与实现(源码+数据库+文档)
java·数据库·小程序·vue·论文·毕设·租房系统管理平台
独行soc6 小时前
2025年渗透测试面试题总结-66(题目+回答)
java·网络·python·安全·web安全·adb·渗透测试
脑子慢且灵7 小时前
[JavaWeb]模拟一个简易的Tomcat服务(Servlet注解)
java·后端·servlet·tomcat·intellij-idea·web