public class Shop {
public double getPrice(String product) throws InterruptedException {
//查询商品的数据库,或链接其他外部服务获取折扣
Thread.sleep(1000);
return new Random().nextDouble() * product.charAt(0) + product.charAt(1);
}
}
当调用这个方法时,它会阻塞进程,等待事件完成。
将同步方法转换成异步方法
public Future<Double> getPriceAsync(String product){
//创建CompletableFuture对象
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
new Thread (()->{
try {
//在另一个线程中执行计算
double price = getPrice(product);
//需要长时间计算的任务结束并得出结果时,设置future的返回值
futurePrice.complete(price);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
return futurePrice;
}
然后可以这样调用:
System.out.println("begin");
Future<Double> futurePrice = shop.getPriceAsync("ss");
System.out.println("doSomething");
System.out.println(futurePrice.get());
System.out.println("end");
begin
doSomething
171.47509091822835
end
这个例子中,首先会调用接口 立即返回一个Future对象,在这种方式下,在查询价格的同时,还可以处理其他任务。最后所有的工作都已经完成,然后再调用future的get方法。获得Future中封装的值,要么发生阻塞,直到该任务异步任务完成,期望的值能够返回。
错误处理
如果没有意外,这个代码工作的会非常正常。但是如果计算价格的过程中发生了错误,那么get会永久的被阻塞。这时可以使用重载的get方法,让它超过一个时间后就强制返回。应该尽量在代码中使用这种方式来防止程序永久的等待下去。超时会引发TimeoutException。但是这样会导致你无法知道具体什么原因导致Future无法返回,这时需要使用CompletableFUture的completeExceptionally方法将导致CompletableFuture内发生的问题抛出。
public Future<Double> getPriceAsync(String product){
//创建CompletableFuture对象
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
new Thread (()->{
try {
double price = getPrice(product);
futurePrice.complete(price);
} catch (Exception ex) {
//抛出异常
futurePrice.completeExceptionally(ex);
}
}).start();
return futurePrice;
}
调用时:
System.out.println("begin");
Future<Double> futurePrice = shop.getPriceAsync("ss");
System.out.println("doSomething");
try {
System.out.println(futurePrice.get(1, TimeUnit.SECONDS));
} catch (TimeoutException e) {
System.out.print(e);
}
System.out.println("end");
设置超时时间,然后会将错误信息打印出来。
工厂方法supplyAsync创建CompletableFuture
使用工厂方法可以一句话来创建getPriceAsync方法
public Future<Double> getPriceAsync(String product) {
return CompletableFuture.supplyAsync(() -> getPrice(product));
}
supplyAsync方法接受一个生产者(Supplier)作为参数,返回一个CompletableFuture对象,该对象完成异步执行后悔读取调用生产者方法的返回值。生产者方法会交由ForkJoinPool池中的某个执行线程(Executor)运行,也可以调用supplyAsync方法的重载版本,传入第二个参数指定不同的线程执行生产者方法。 工厂方法返回的CompletableFuture对象也提供了同样的错误处理机制。
阻塞优化
例如现在有一个商品列表,然后输出一个字符串 商品名,价格 。