在上一节中,了解了关于CompletableFuture执行异步任务的方法,但是这些方法在执行完异步任务之后,这个异步任务的结果怎么
获取呢?
在CompletableFuture异步任务执行完之后,返回的是一个CompletableFuture对象,在以前Future时代,我们通过future.get()获取其
异步任务执行的结果,但是这个方法是阻塞的,也就是说:当你future.get()的时候,请求线程是阻塞的(主线程{父线程}),必须等待
子线程完成结果以后,才会执行主线程的任务,此时其实是同步的,所以为了改进CompletableFuture做了..
类结构:
public class CompletableFuture<T> implements Future<T>, CompletionStage<T> {}
在执行完异步任务之后,我们理解下面的回调函数:
1.从有没有返回值的角度去分析:
1.public <U> CompletableFuture<U> thenApply(
Function<? super T,? extends U> fn) {
return uniApplyStage(null, fn);
}
2.public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {
return uniAcceptStage(null, action);
}
3.public CompletableFuture<Void> thenRun(Runnable action) {
return uniRunStage(null, action);
}
private <V> CompletableFuture<V> uniApplyStage(
Executor e, Function<? super T,? extends V> f) {
if (f == null) throw new NullPointerException();
CompletableFuture<V> d = new CompletableFuture<V>();
if (e != null || !d.uniApply(this, f, null)) {
UniApply<T,V> c = new UniApply<T,V>(e, d, this, f);
push(c);
c.tryFire(SYNC);
}
return d;
}
一个关键的因素不要忘记:如果没有用户(程序员本身)没有创建线程池,则会默认的使用forkJoin,
有没有返回值,关键是从入参开始:
1.thenApply接受的是一个Function<? super T,? extends U> fn,而Function本身是一个函数式的接口如下,重点是apply方法,传入一个
T,返回一个R,这个是泛型的类型
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
2.thenAccept接受的是一个Consumer<? super T> action,而Consumer本身也是一个函数式的接口,顾名思义,是消费,传入一个T类型
没有返回值
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
}
3.thenRun(Runnable action)同理上面的
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
至于其他的方法道理都是一样的,
即:不管是异步还是同步,都有一个线程池的参数对象
比如:
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);
}
比如: 当一个线程依赖另一个线程时,可以使用 thenApply 方法来把这两个线程串行化。 Function<? super T,? extends U> T:上一个任务返回结果的类型 U:当前任务的返回值类型 private static void thenApply() throws ExecutionException, InterruptedException { CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> { long result = new Random().nextInt(100); System.out.println("result : " + result); return result; }).thenApply((res) -> { System.out.println("res "+ res); return res*5; }); Long aLong = future.get(); System.out.println(aLong);
总体的一个思路是:
异步任务的执行底层是用线程池实现的,而异步任务的回调是通过上面的方法实现的,而最终的结果时什么呢?
下面继续...