话说上回,我们认识了基本的线程、进程,Java线程的发展历程,以及JDK1.8中线程实现类CompletetableFuture中创建线程、获取结果集的方法。在下半部分中我们需要进一步了解类中常用的方法。同时为了更好的了解各种方法使用,我会对某些方法进行代码实例讲解,由于JDK1.8更新CompletableFuture同时推出了Lambda表达式,所以对其中代码的理解需要Lambda的基础。
更优的结果获取方法
- T get(long timeout,TimeUnit unit)
timeout设置等待时间,unit是时钟,当线程等待获取结果的时间超过了timeout,就会抛出异常InterruptedException, ExecutionException, TimeoutException,TimeUnit是一个枚举类,所以传入它的对象直接传入它的枚举成员。
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
CompletableFuture.supplyAsync(() -> {try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}return 1;})
.get(2, TimeUnit.SECONDS);
}
//这里我们假设需要10s才能完成任务,使用这个方法就能提前终止并抛出异常
- T getNow(T valueIfAbsent)
当没有结果的时候,返回替补值valueIfAbsent。
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
System.out.println(new CompletableFuture<Integer>().supplyAsync(() -> {try {
TimeUnit.SECONDS.sleep(12);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;})
.getNow(2));;
}
//当执行到getNow的时候,如果并没有返回结果集,就返回参数值valueIfAbsent
对结果进行处理
- thenApply(Function fc)
对上一步的结果进行任意操作,相当于任务分阶段进行。
- handle(BiFunction bfc)
和
thenApply一样,但是它在遇到异常的时候不会终止,而是跳过出错的环节进入函数回调的下一步,并且它的参数是两项,一个参数是任意的(多数是上一次任务的结果),另一个是异常对象。
对结果进行消费
- thenAccept(Consumer c)
需要上一阶段的结果,返回的
CompletableFuture是void类型的
- thenRun(Runnable r)
不需要上一阶段的结果,返回的
CompletableFuture是void类型的
CompletableFuture a = new CompletableFuture<Integer>().supplyAsync(() -> {try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;});
a.thenAccept((t) -> System.out.println(t));
System.out.println(a.get());
}
//如果向上述的线程的使用中,没有用连续的方法链(流式的写法),是不会对结果进行消费
//的,因为每一步执行方法返回的CompletableFuture是执行完任务之后新的对象
//所以需要改为
CompletableFuture a = new CompletableFuture<Integer>().supplyAsync(() -> {try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 1;})
.thenAccept((t) -> System.out.println(t));
System.out.println(a.get());
}
//才会对结果进行真正的消费
//而在链式操作中使用了thenAccept或者thenRun,都会使得结果集被消费,无论链中哪个步骤的get()方法都只会返回null
1.上面两种方法需要等待上一阶段任务完成,同时调用的是上一阶段使用的线程池的同一个线程。
2.对上面两个方法加上后缀Async,那么之前如果使用的是自定义的线程池,使用thenRunAsync一定会走默认线程池。
3.(使用自定义线程池)如果上一阶段任务处理很快,并且main执行了线程池关闭,那么后面的过程不会执行。
4.如果上一阶段处理任务很快,那么后续的thenRun可能会使用main线程。
CompletableFuture
.supplyAsync(() -> {System.out.println(Thread.currentThread().getName());return "s";})
.thenRun(() -> System.out.println(Thread.currentThread().getName()));
有几率输出:
ForkJoinPool.commonPool-worker-9
main
对计算速度进行选用
- applyToEither(CompletionStage<? extends String> t,Function<? super String,U> f)
t参数放入另一个线程。 f接收线程返回结果(两个线程谁先return就接收谁的),并进行处理。 例如双人对战小游戏中,我们需要两个角色的各种基础逻辑是独立的,但是两个角色谁的血量先低于0谁死亡,那么这个方法就是类似这样的逻辑。
对计算结果进行合并
- thenCombine(CompletionStage t,BiFunction bf)
将两个线程结果进行合并;bf接收两个线程结算结果。将各科老师阅卷看成各个线程,当一个人的各项成绩出来之后,就会计算总成绩。而这里就体现出线程结果的合并操作。
方法小结
对于CmplettableFuture类,有很多实现的方法,方法核心在于接口CompletionStage,接口中的方法大致分为supply,Function,BiFunction,Consumer,Runnable四种类型,同时还有加上各自方法名加上Async的新方法。这些方法需要我们自己去理解。
本人知识有限,敬请各位指正。