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

需求

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

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

相关推荐
AAD555888996 小时前
数字仪表LCD显示识别与读数:数字0-9、小数点及单位kwh检测识别实战
python
开源技术8 小时前
Python Pillow 优化,打开和保存速度最快提高14倍
开发语言·python·pillow
学嵌入式的小杨同学8 小时前
从零打造 Linux 终端 MP3 播放器!用 C 语言实现音乐自由
linux·c语言·开发语言·前端·vscode·ci/cd·vim
毕设源码-朱学姐9 小时前
【开题答辩全过程】以 基于JavaWeb的网上家具商城设计与实现为例,包含答辩的问题和答案
java
weixin_425543739 小时前
TRAE CN3.3.25 构建的Electron简易DEMO应用
前端·typescript·electron·vite·nestjs
Li emily9 小时前
解决港股实时行情数据 API 接入难题
人工智能·python·fastapi
wfeqhfxz25887829 小时前
农田杂草检测与识别系统基于YOLO11实现六种杂草自动识别_1
python
Mr Xu_10 小时前
【Vue3 + ECharts 实战】正确使用 showLoading、resize 与 dispose 避免内存泄漏
前端·信息可视化·vue·echarts
mftang10 小时前
Python 字符串拼接成字节详解
开发语言·python
0思必得010 小时前
[Web自动化] Selenium设置相关执行文件路径
前端·爬虫·python·selenium·自动化