【同步API】
同步API是我们传统方法,调用方法在被调用方法运行的过程中会一直等待,直到被调用方法返回。此时的调用过程,是阻塞式的调用。
【异步API】
与同步API相反,异步API会直接返回,或者至少在被调用方计算完成之前,将它剩余的计算任务交给另外一个线程去做,该线程和调用方是异步的,是非阻塞式调用。
来看一下【同步API】与【异步API】的区别
public class Shop {
public double getPrice(String product) {
return calculatePrice(product);
}
public Future<Double> getPriceAsync(String product) {
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
new Thread(() -> {
double price = calculatePrice(product);
futurePrice.complete(price);
}).start();
return futurePrice;
}
private double calculatePrice(String product) {
delay();
return new Random().nextDouble() * product.charAt(0) * product.charAt(1);
}
public static void delay() {
try {
Thread.sleep(1000L);
} catch (InterruptedException interruptedException) {
throw new RuntimeException(interruptedException);
}
}
}
在上面的代码中,getPrice()
是一个同步方法,其内部调用的calculatePrice()
用于模拟价格计算,计算过程中,暂停线程1s
用于模拟阻塞。
然后我们调用getPrice()
,查看程序执行时间。
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Shop shop = new Shop();
long startTime = System.currentTimeMillis();
double doubleFuture = shop.getPrice("apple");
doSomeThingElse();
long endTime = System.currentTimeMillis();
System.out.println("总耗时:" + (endTime - startTime) + "ms");
}
private static void doSomeThingElse(){
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
此时查看控制台,我们可以看到共执行了3011ms
,也就是说,此时整个程序是串行的。
此时我们再来看看另一个方法getPriceAsync()
,此方法不再返回double
类型,而是一个Future<Double>
,Future<T>
接口标识一个异步计算的结果,泛型代表返回值。Future
的计算结果,通过get()
方法获取。
在getPriceAsync()
方法中,我们创建了一个代表异步计算的CompletableFuture<T>
,它在计算完成后,会包含计算的结果。接着创建一个线程去执行实际的操作,不必等到线程结束,直接返回一个Future<Double>
实例。当线程计算结束后,调用CompletableFuture<T>
的complete()
方法,结束CompletableFuture<T>
的运行。
然后我们调用getPriceAsync()
方法,查看程序执行时间。
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
Shop shop = new Shop();
long startTime = System.currentTimeMillis();
Future<Double> doubleFuture = shop.getPriceAsync("apple");
doSomeThingElse();
doubleFuture.get(2, TimeUnit.SECONDS);
long endTime = System.currentTimeMillis();
System.out.println("总耗时:" + (endTime - startTime) + "ms");
}
private static void doSomeThingElse(){
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
此时查看控制台,可以看到总体耗时2108ms
,也就是说我们主程序跟getPriceAsync()
方法此时是并行的了。
【改进的异步方法】
在前面的getPriceAsync()
方法中,我们创建了一个线程创建CompletableFuture<T>
对象,实际上,还有一种更简便的方式,即使用工厂方法CompletableFuture.supplyAsync
创建CompletableFuture<T>
对象。
改进后的代码如下所示
public Future<Double> getPriceAsync(String product) {
// CompletableFuture<Double> futurePrice = new CompletableFuture<>();
// new Thread(() -> {
// double price = calculatePrice(product);
// futurePrice.complete(price);
// }).start();
// return futurePrice;
return CompletableFuture.supplyAsync(()-> calculatePrice(product));
}
评论 (0)