需求
依据指定产品名称返回价格
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