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

需求

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

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

相关推荐
西柚小萌新13 分钟前
【深入浅出PyTorch】--上采样+下采样
人工智能·pytorch·python
未来之窗软件服务1 小时前
自己写算法(九)网页数字动画函数——东方仙盟化神期
前端·javascript·算法·仙盟创梦ide·东方仙盟·东方仙盟算法
程序员小凯1 小时前
Spring Boot测试框架详解
java·spring boot·后端
豐儀麟阁贵1 小时前
基本数据类型
java·算法
_extraordinary_2 小时前
Java SpringMVC(二) --- 响应,综合性练习
java·开发语言
你的人类朋友2 小时前
什么是断言?
前端·后端·安全
shut up2 小时前
LangChain - 如何使用阿里云百炼平台的Qwen-plus模型构建一个桌面文件查询AI助手 - 超详细
人工智能·python·langchain·智能体
程序员 Harry2 小时前
深度解析:使用ZIP流式读取大型PPTX文件的最佳实践
java
宝贝儿好3 小时前
【python】第五章:python-GUI编程
python·pyqt
FIN66683 小时前
昂瑞微:实现精准突破,攻坚射频“卡脖子”难题
前端·人工智能·安全·前端框架·信息与通信