文章目录
如果你的意图是实现并发,而非并行,或者你的主要目标是在同一个CPU上执 行几个松耦合的任务,充分利用CPU的核,让其足够忙碌,从而大化程序的吞吐量。
那么你其实真正想做的是避免因为等待远程服务的返回,或者对数据库的查询,而阻塞线程的执行, 浪费宝贵的计算资源,因为这种等待的时间很可能相当长。
比如想查询一个商品的详情,包括价格。查询价格是另一个服务,但是又不想因为查询价格阻塞其他信息的查询(比如库存量)。那么可以了解一下CompletableFuture。
Future接口
Future接口在Java5中被引入,可以实现一种异步计算,返回一个执行运算结果的引用,当运算结束后,这个引用被返回给调用方。
在 Future中触发那些潜在耗时的操作把调用线程解放出来,让它能继续执行其他有价值的工作, 不再需要呆呆等待耗时的操作完成。打个比方,你可以把它想象成这样的场景:你拿了一袋子衣 服到你中意的干洗店去洗。干洗店的员工会给你张发票,告诉你什么时候你的衣服会洗好(这就 是一个Future事件)。衣服干洗的同时,你可以去做其他的事情。
Long start = System.nanoTime();
ExecutorService executor = Executors.newCachedThreadPool();
Future<Integer> future = executor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return doSomeLongCompution();
}
});
doSomethingElse();
System.out.println((System.nanoTime() - start)/1000000);
try {
Integer result = future.get(5, TimeUnit.SECONDS);
System.out.println((System.nanoTime() - start)/1000000);
} catch (Exception e) {
e.printStackTrace();
}
这种编程方式让你的线程可以在ExecutorService以并发方式调用另一个线程执行耗时操作的同时,去执行一些其他的任务。
接着,如果你已经运行到没有异步操作的结果就无法继续任何有意义的工作时,可以调用它的get方法去获取操作的结果。
如果操作已经完成,该方法会立刻返回操作的结果,否则它会阻塞你的线程,直到操作完成,返回相应的结果。
这个场景存在一个问题,如果该长时间运行的操作永远不返回会一直阻塞,所以推荐使用get方法的时候可以加一个超时参数。避免一直等待。
Future接口有一定的局限性,比如第二个异步计算依赖于第一个异步计算。又或者仅等待Future集合中最快结束的任务完成。
使用CompletableFuture 构建异步应用
supplyAsync
supplyAsync 方法接受一个生产者(Supplier)作为参数,返回一个 CompletableFuture
对象
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {
return asyncSupplyStage(asyncPool, supplier);
}
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) {
return asyncSupplyStage(screenExecutor(executor), supplier);
}
其中Async结尾的方法时可以异步执行的,如果指定了线程池,会在指定的线程池中执行,如果没有指定,使用默认的。
public static final Map<String, Double> map = ImmutableMap.of("手机", new Double(65), "面包", new Double(60));
public Future<Double> getPriceAsyc(String product){
return CompletableFuture.supplyAsync(() -> calculatePrice(product));
}
private double calculatePrice(String product) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return map.get(product);
}
public static void main(String[] args) throws Exception {
QueryTest q = new QueryTest();
CompletableFuture<Double> futurePrice = q.getPriceAsyc("面包");
CompletableFuture<Double> futurePrice2 = q.getPriceAsyc("手机");
System.out.println(futurePrice.get(5, TimeUnit.SECONDS));
}
thenCompose和thenCombine
你对一个CompletableFuture对象调用了thenCompose方法,并向其传递了第二个CompletableFuture,而第二个CompletableFuture又需要使用第一个CompletableFuture的执行结果作为输入。
但是,另一种比较常见的情况是,你需要将两个完全不相干的CompletableFuture对象的结果整合起来,而且你也不希望等到第一个任务完全结 束才开始第二项任务。这时使用thenCombine。
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(null, fn);
}
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) {
return uniComposeStage(asyncPool, fn);
}
public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,Executor executor) {
return uniComposeStage(screenExecutor(executor), fn);
}
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) {
return biApplyStage(null, other, fn);
}
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn) {
return biApplyStage(asyncPool, other, fn);
}
public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn, Executor executor) {
return biApplyStage(screenExecutor(executor), other, fn);
}
//thenCompose
QueryTest q = new QueryTest();
CompletableFuture<Double> futurePrice = q.getPriceAsyc("面包");
CompletableFuture<Double> futurePriceCovert = futurePrice.thenCompose(price -> CompletableFuture.supplyAsync(() -> covertPrice(price)));
System.out.println(futurePriceCovert.get(5, TimeUnit.SECONDS));
//thenCombine
CompletableFuture<Double> futurePriceCombine = futurePrice.thenCombine(q.getPriceAsyc("手机"), (r1, r2) -> r1 + r2);
System.out.println(futurePriceCombine.get(5, TimeUnit.SECONDS));
thenApply
入参是一个Function函数,可以变换之前计算的结果
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) {
return uniApplyStage(asyncPool, fn);
}
public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) {
return uniApplyStage(screenExecutor(executor), fn);
}
CompletableFuture<Double> futurePriceThen = CompletableFuture.supplyAsync(() -> covertPrice(new Double(60))).thenApply(price -> price + 100);
System.out.println(futurePriceThen.get(5, TimeUnit.SECONDS));
thenAccept
入参是一个Consumer函数
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {
return uniAcceptStage(asyncPool, action);
}
public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor) {
return uniAcceptStage(screenExecutor(executor), action);
}
CompletableFuture.supplyAsync(() -> covertPrice(new Double(60))).thenAccept(System.out::print);
join
等待所有异步线程结束
List<CompletableFuture<String>> priceFutures =...
priceFutures.stream().map(CompletableFuture::join) .collect(toList());
Executor
有时为了更好的使用线程池,可以根据需要创建合适个数的线程池
假设:有一组列表shops,最大线程池数量不超过100
private final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size, 100), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}
});