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

需求

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

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:

为了让客户端能了解商店无法提供请求商品价格的原因,你需要使用CompletableFuture的completeExceptionally方法将导致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

相关推荐
这个昵称不容易1 小时前
SpringBoot「衣依」服装销售平台:144个Java文件180个接口,从挑衣服到收货评价整条链路都在
java·springboot·毕设
计算机毕设定制辅导-无忧学长2 小时前
《基于微信小程序的居家医疗陪护预约系统设计与实现》
java·spring boot·微信小程序·uniapp·居家医疗陪护预约系统
羲云网络2 小时前
GPT Image 2.6 接入指南:Flare、Sunburst 怎么选,编辑失败如何排查
人工智能·python·aigc·图像生成·acedatacloud
去伪存真2 小时前
AI Agent Harness 深度测评与技术选型实战
前端·agent
文丑颜不良啊3 小时前
LeetCode2221~2240题目解析
java·leetcode
秋饼3 小时前
Computer Use Agent Java 落地实战:感知-行动循环、执行器与安全围栏生产级实现
java·ai·技术分享·后端开发
文丑颜不良啊5 小时前
LeetCode2161~2180题目解析
java·leetcode
FRing035 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
橙橙笔记5 小时前
具身智能的学习 - 完成一个机械臂 第1节
python·学习·机械臂·具身智能
小小张说故事5 小时前
Python 的 class 语句到底执行了什么?拆开 __prepare__、metaclass.__new__ 和那个被讲反的 __set_name__ 顺序
python