Future接口能干什么
Future是Java5新加的一个接口,它提供一种异步并行计算的功能,如果主线程需要执行一个很耗时的计算任务,我们会就可以通过Future把这个任务放进异步线程中执行,主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。
Future接口相关架
目的:异步多线程任务执行且返回有结果,三个特点:多线程、有返回、异步任务(班长为老师去买水作为新启动的异步多线程任务且买到水有结果返回)
●代码实现:Runnable接口+Callable接口+Future接口和FutureTask实现类。
Future 例子:
public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<String> futureTask = new FutureTask<>(new mythread()); Thread thread = new Thread(futureTask); thread.start(); // get()阻塞---一旦调用get()方法求结果,一旦调用不见不散,非要等到结果才会离开, // 不管你是否计算完成,如果没有计算完成容易程序堵塞。 // System.out.println(futureTask.get()); while(true){ // isDone()轮询---轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果, // 如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞。 if (futureTask.isDone()){ //去获取结果有无 System.out.println(futureTask.get()); break; }else{ //做主线程的业务 Thread.sleep(500); System.out.println("正在处理中,不要催了,越催越慢"); } } } public static class mythread implements Callable<String>{ @Override public String call() throws Exception { Thread.sleep(2000); System.out.println("come"); return "null"; } }
-
优点:Future+线程池异步多线程任务配合,能显著提高程序的运行效率。
-
缺点:
-
get()阻塞---一旦调用get()方法求结果,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,如果没有计算完成容易程序堵塞。
-
isDone()轮询---轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果,如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞。
-
结论:Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果。
-
对于简单的业务场景使用Future完全ok
-
回调通知:
-
应对Future的完成时间,完成了可以告诉我,也就是我们的回调通知
-
通过轮询的方式去判断任务是否完成这样非常占cpu并且代码也不优雅
-
创建异步任务:Future+线程池组合
-
多个任务前后依赖可以组合处理(水煮鱼--->买鱼--->调料--->下锅):
-
想将多个异步任务的结果组合起来,后一个异步任务的计算结果需要钱一个异步任务的值
-
想将两个或多个异步计算合并成为一个异步计算,这几个异步计算互相独立,同时后面这个又依赖前一个处理的结果
-
对计算速度选最快的:
-
当Future集合中某个任务最快结束时,返回结果,返回第一名处理结果
-
结论:
-
使用Future之前提供的那点API就囊中羞涩,处理起来不够优雅,这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求。
-
从i到i++
-
Future能干的,CompletableFuture都能干
CompletableFuture对Future的改进
-
get()方法在Future计算完成之前会一直处在阻塞状态下,阻塞的方式和异步编程的设计理念相违背。
-
isDene()方法容易耗费cpu资源(cpu空转),
-
对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动调用该回调函数,这样,我们就不用等待结果
jdk8设计出CompletableFuture,CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。
CompletableFuture和CompletionStage介绍
类架构说明:
-
接口CompletionStage
-
代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段。
-
一个阶段的执行可能是被单个阶段的完成触发,也可能是由多个阶段一起触发
-
类CompletableFuture
-
提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合CompletableFuture的方法
-
它可能代表一个明确完成的Future,也可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作
核心的四个静态方法,来创建一个异步任务
四个静态构造方法
对于上述Executor参数说明:若没有指定,则使用默认的ForkJoinPoolcommonPool()作为它的线程池执行异步代码,如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码
/** * @author 15032 */ public class Completablefuturedemo { public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(()->{ System.out.println("runnabletest"); }); System.out.println(voidCompletableFuture.get()); ExecutorService executorService = Executors.newFixedThreadPool(3); CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{ System.out.println("runnabletest1"+Thread.currentThread().getName()); },executorService); CompletableFuture<String> completableFuture2 = CompletableFuture.supplyAsync(()->{ System.out.println("runnabletest2"+Thread.currentThread().getName()); return "有返回值"; },executorService); System.out.println(completableFuture2.get()); executorService.shutdown(); } }
CompletableFuture常用方法
-
获得结果和触发计算
-
获取结果
-
public T get()
-
public T get(long timeout,TimeUnit unit)
-
public T join() --->和get一样的作用,只是不需要抛出异常
-
public T getNow(T valuelfAbsent) --->计算完成就返回正常值,否则返回备胎值(传入的参数),立即获取结果不阻塞
-
主动触发计算
-
public boolean complete(T value) ---->是否打断get方法立即返回括号值
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(3); CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("come"); try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } return "asdasd"; },executorService); Thread.sleep(1000); System.out.println(stringCompletableFuture.complete("999")+stringCompletableFuture.get()); executorService.shutdown(); }
-
对计算结果进行处理
-
thenApply --->计算结果存在依赖关系,这两个线程串行化---->由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停
-
handle --->计算结果存在依赖关系,这两个线程串行化---->有异常也可以往下走一步
public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(3); CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> { System.out.println("come"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } return "asdasd"; },executorService).thenApply((v)-> v+"thenApply").thenApply((v)->{ int i = 1/0; return v+"Throwable"; }).handle((f,e)->{ return f+"handle"; }); System.out.println(stringCompletableFuture.get()); executorService.shutdown(); }
-
对计算结果进行消费
-
接受任务的处理结果,并消费处理,无返回结果
-
thenAccept
public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(3); CompletableFuture.supplyAsync(() -> { return 1; }, threadPool).thenApply(f -> { return f + 2; }).thenApply(f -> { return f + 2; }).thenAccept(r -> { System.out.println(r);//5 }); }
-
对比补充
-
thenRun(Runnable runnable) :任务A执行完执行B,并且不需要A的结果
-
thenAccept(Consumer action): 任务A执行完执行B,B需要A的结果,但是任务B没有返回值
-
thenApply(Function fn): 任务A执行完执行B,B需要A的结果,同时任务B有返回值
public class CompletableFutureApi2Demo { public static void main(String[] args) { System.out.println(CompletableFuture.supplyAsync(() -> "result").thenRun(() -> {}).join());//null System.out.println(CompletableFuture.supplyAsync(() -> "result").thenAccept(r -> System.out.println(r)).join());//result null System.out.println(CompletableFuture.supplyAsync(() -> "result").thenApply(f -> f + 2).join());//result2 } }
-
CompletableFuture和线程池说明
-
如果没有传入自定义线程池,都用默认线程池ForkJoinPool
-
传入一个线程池,如果你执行第一个任务时,传入了一个自定义线程池
-
调用thenRun方法执行第二个任务时,则第二个任务和第一个任务时共用同一个线程池
-
调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自定义的线程池,第二个任务使用的是ForkJoin线程池
-
备注:可能是线程处理太快,系统优化切换原则, 直接使用main线程处理,thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,之间的区别同理。
-
对计算结果进行合并
-
两个CompletableStage任务都完成后,最终能把两个任务的结果一起交给thenCombine来处理
-
先完成的先等着,等待其他分支任务
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> objectCompletableFuture1 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("first come"); return "first "; }); CompletableFuture<String> objectCompletableFuture2 = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println("second come"); return "second "; }); System.out.println(objectCompletableFuture1.thenCombine(objectCompletableFuture2, (a, b) -> a + b).get()); }
-
对计算速度进行选用
-
谁快用谁
-
applyToEither
public static void main(String[] args) throws ExecutionException, InterruptedException { CompletableFuture<String> objectCompletableFuture1 = CompletableFuture.supplyAsync(() -> { try { System.out.println("first come"); Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } return "first "; }); CompletableFuture<String> objectCompletableFuture2 = CompletableFuture.supplyAsync(() -> { try { System.out.println("second come"); Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } return "second "; }); CompletableFuture<String> completableFuture = objectCompletableFuture1.applyToEither(objectCompletableFuture2, f -> f); System.out.println(completableFuture.get()); }