前言
在实际开发过程中,难免会遇到诸如文件上传、下载等耗时且用户不需要重点关注的需求,让用户一直等待操作完成显然不是最优解决方案,这时就需要我们开启一个异步任务去处理后台任务,让用户不再等待,下面,从原始的Future接口说起,逐步深入了解CompletableFuture接口的相关特性和应用。
一、Future回顾
Future接口是jdk5提出的异步任务解决方案,定义了操作异步任务执行一些方法,例如检查计算是否完成、等待计算完成以及检索计算结果的方法等。
异步任务有三个特点:多线程、有返回、异步,因此,想要实现异步任务,还需要多线程处理,这就涉及到了Runable接口和Callable接口,我们直到,Callable接口是有返回的,因此,我们在开启异步任务时,创建的线程是声明Callable接口的,下面,看下Future接口的实现FutureTask的相关引用
在上图中,FutureTask实现了Runable相关接口,和我们上面的说法冲突,难道上述说法不正确吗?看下FutureTask的构造器,有两个
package com.example.test.CompletableFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new Thread1());
new Thread(()->{
futureTask.run();
},"t1").start();
System.out.println(Thread.currentThread().getName()+"执行其他任务");
System.out.println(Thread.currentThread().getName()+":"+futureTask.get());
}
}
class Thread1 implements Callable{
@Override
public Object call() throws Exception {
System.out.println(Thread.currentThread().getName()+"-----------get-----------");
return "hello world!";
}
}
创建一个实现callable接口的线程t1,让它去执行任务,然后获取它的结果,运行结果如下
可以看到,执行异步任务的线程和打印结果的线程不是同一个,主线程去执行其他任务了,那么,如果我们有多个任务,是不是每次都要开启一个线程去执行呢?显然不是的,频繁的开启线程会带来额外的开销,因此,我们使用线程池去处理,加入我们有三个任务,先看下顺序执行的耗时
public static void m1(){
//一个主线程,执行3个任务时间
long startTime = System.currentTimeMillis();
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
long endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"毫秒-----");
}
然后我们创建一个3个线程的定长线程池,这里为了方便,我是用Executors类创建,实际应用线程池不推荐这样操作
public static void m2() throws ExecutionException, InterruptedException {
long startTime = System.currentTimeMillis();
//多个异步线程执行任务
FutureTask<String> futureTask1 = new FutureTask<String>(()->{
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "task1 over";
});
ExecutorService threadPool = Executors.newFixedThreadPool(3);
threadPool.submit(futureTask1);
FutureTask<String> futureTask2 = new FutureTask<String>(()->{
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "task2 over";
});
threadPool.submit(futureTask2);
FutureTask<String> futureTask3 = new FutureTask<String>(()->{
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "task3 over";
});
threadPool.submit(futureTask3);
//get方法阻塞主线程执行,一般放在程序主线程后面,也可以设置等待超时时间
//通常使用轮询方式去查询结果,通过isDone()方法去判断异步任务是否执行完成,执行完成再去get()
System.out.println(futureTask1.get());
System.out.println(futureTask2.get());
System.out.println(futureTask3.get());
long endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"毫秒-----");
System.out.println(Thread.currentThread().getName());
threadPool.shutdown();
}
可以看到,耗时大大缩短,这也就看出Future的优点:Future+线程池异步多线程任务配合,能显著提高程序的运行效率。请注意上面代码的中,我获取执行结果统一放到了最后,这是因为get()
方法,会阻塞线程的执行,直到获取结果,如果每个任务后面紧挨着获取结果,那就变成了顺序任务,执行时间和m1()
无异。那么,怎么可以判断异步任务是否执行完成呢?Future接口中提供了isDone()
方法,我们可以轮询isDone()
去判断是否执行完成,可是这样就会造成资源浪费,由此引出了Future接口的两个问题:
get()
阻塞—一旦调用get()方法求结果,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,如果没有计算完成容易程序堵塞isDone()
轮询—轮询的方式会耗费无谓的cpu资源,而且也不见得能及时得到计算结果,如果想要异步获取结果,通常会以轮询的方式去获取结果,尽量不要阻塞
可以看到,Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果,那么有没有一种更好的方法去获取结果呢?那就是今天的主角CompletableFuture。
二、CompletableFuture入门
先看下CompletableFuture的类图
可以看到它实现了Future接口和CompletionStage接口,实现Future接口我们很容易理解,那么CompletionStage是做什么的呢?上面我们说过Future不能应付任务完成通知(非轮询)、多个任务依赖组合处理(将多个异步任务结果组合起来,后任务依赖前任务结果)等复杂场景,而CompletableFuture可以实现如下功能:
- 异步任务结束,会自动回调某个对象方法
- 主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行
- 异步任务出错时,会自动回调某个对象方法
它之所以这么强大,就是因为实现了CompletionStage接口功能。下面,先看下ConpletableFuture如何使用,和FutureTask不同的是,CompletableFuture不是通过new的方式操作的,它提供了四个静态方法初始化,分为两大类:runAsync(无返回值)和supplyAsync(有返回值),分别无线程池形参和有线程池形参
/**
* 根据四个静态方法创建,两大类:runAsync(无返回值)和supplyAsync(有返回值),分别无线程池形参和有线程池形参
* 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);
* }
*
* public static CompletableFuture<Void> runAsync(Runnable runnable) {
* return asyncRunStage(asyncPool, runnable);
* }
*
* public static CompletableFuture<Void> runAsync(Runnable runnable,
* Executor executor) {
* return asyncRunStage(screenExecutor(executor), runnable);
* }
*/
public static void init() throws ExecutionException, InterruptedException {
/**
* 无返回值
*/
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(()->{
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
System.out.println(voidCompletableFuture.get());
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(()->{
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},threadPool);
System.out.println(completableFuture.get());
threadPool.shutdown();
/**
* 有返回值
*/
CompletableFuture<String> objectCompletableFuture = CompletableFuture.supplyAsync(()->{
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "hello world!";
});
System.out.println(objectCompletableFuture.get());
ExecutorService threadPool1 = Executors.newFixedThreadPool(3);
CompletableFuture<String> objectCompletableFuture1 = CompletableFuture.supplyAsync(()->{
try {
System.out.println(Thread.currentThread().getName());
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "hello world!";
},threadPool1);
System.out.println(objectCompletableFuture1.get());
threadPool1.shutdown();
}
既然CompletableFuture实现了Future接口,那么如何操作CompletableFuture作为Future接口使用呢?
public static void use() throws ExecutionException, InterruptedException {
CompletableFuture<Integer> integerCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "--------come in");
int i = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("----------1秒钟后出结果:" + i);
return i;
});
System.out.println(Thread.currentThread().getName()+"线程先去忙别的任务");
System.out.println(integerCompletableFuture.get());
}
接着看下CompletableFuture的通知和承上启下如何实现
public static void notifyResult(){
ExecutorService threadPool = Executors.newFixedThreadPool(3);
try{
CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "--------come in");
int i = ThreadLocalRandom.current().nextInt(10);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("----------3秒钟后出结果:" + i);
/**
* 模拟异常
*/
// if(i>1){
// int r = 10/0;
// }
return i;
},threadPool).whenComplete((re,ex)->{
/**
* re代表上一步任务的值
* ex代表上一步是否有异常
*/
if(ex == null){
System.out.println("----计算完成,更新系统值:"+re);
}
}).exceptionally(ex->{
ex.printStackTrace();
System.out.println("异常情况:"+ex.getCause()+"\t"+ex.getMessage());
return null;
});
System.out.println(Thread.currentThread().getName()+"线程先去忙别的任务");
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
/**
* 主线程不要立即结束,否则CompletableFuture默认使用线程池会立刻关闭,不能返回结果,或者使用自定义线程池
*/
// try {
// TimeUnit.SECONDS.sleep(3);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
}
我们可以看到,在异步任务后面有一个whenComplete
方法,它有两个参数,分别为执行结果和异常,我们获取上一个异步任务的结果就是通过这个方法实现,同时,根据有无异常,我们还可以调用不同的回调方法。这里有一个注意的点是,我传入了自定义的线程池,这是因为,不传入自定义线程池的话,假如主线程立即结束,CompletableFuture默认使用线程池会立刻关闭,不能返回结果,因此,不传入自定义线程池的话,需要让主线程等待一段时间,这样才能获取返回结果。
三、常用API
CompletableFuture的API很多,基本上可以分为六类,接下来分别简单介绍。
- 获取结果和触发计算
获取结果我们并不陌生,已经使用过的get()
方法就是获取结果,除此之外,还有get(long timeout,TimeUnit unit)
,超过等待时间抛出异常;join()
,同get方法,不用抛异常;getNow(T valueIfAbsent)
获取结果的方法,这里我们着重介绍getNow方法。而触发计算是complete(T value)
方法,返回true/false,代表是否中断了执行中的异步任务,其中,入参value代表若中断了异步任务,返回一个设定值value
public static void m1() throws ExecutionException, InterruptedException, TimeoutException {
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "abc";
});
//get
// System.out.println(stringCompletableFuture.get());
// System.out.println(stringCompletableFuture.get(2L,TimeUnit.SECONDS));
// System.out.println(stringCompletableFuture.join());
/**
* getNow():立即获取结果不阻塞
* 1、计算完成,返回计算完成的结果
* 2、没计算完,返回设定的valueIfAbsent值
*/
// try {
// TimeUnit.SECONDS.sleep(3);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println(stringCompletableFuture.getNow("xxx"));
/**
* complete(T value)方法返回true/false,代表是否中断了执行中的异步任务,其中,入参value代表若中断了异步任务,返回一个设定值value
*/
System.out.println(stringCompletableFuture.complete("test")+"\t"+stringCompletableFuture.join());
}
- 对计算结果进行处理:计算结果存在依赖关系,线程串行化
主要分为两类,thenApply和handle,他们分别有三种实现,主要区别就是,thenApply遇到异常就会停止在出现异常的任务结束,而handle可以处理遇到的异常,继续向下执行,然而由于任务异常,会导致计算过程丢失,因此最终的结果是null
* thenApply:
* public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn)
* public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn)
* public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)
* handle:
* public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn)
* public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn)
* public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor)
*/
public static void m2(){
ExecutorService threadPool = Executors.newFixedThreadPool(3);
CompletableFuture.supplyAsync(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("买鱼");
return 1;
},threadPool).thenApply(f->{
System.out.println("起锅烧油");
return f+2;
})
// .handle((f,e)->{
// //遇到异常,继续向下执行
// int i = 10/0;
// System.out.println("起锅烧油");
// return f+2;
// })
// .handle((f,e)->{
// System.out.println("起锅烧油");
// return f+2;
// })
// .thenApply(f->{
// //遇到异常,终止运行
// int i = 10/0;
// System.out.println("起锅烧油");
// return f+2;
// })
.thenApply(f->{
System.out.println("做酸菜鱼");
return f+3;
}).whenComplete((re,ex)->{
if(null==ex){
System.out.println("计算结果:"+re);
}
}).exceptionally(ex->{
ex.printStackTrace();
return null;
});
threadPool.shutdown();
System.out.println(Thread.currentThread().getName()+" 去忙其他的");
}
- 对计算结果进行消费:接受任务的处理结果,并消费结果,无返回结果
* thenRun:任务A执行完执行任务B,B不需要A的结果
* thenAccept:任务A执行完执行任务B,B需要A的结果,但是任务B无返回
* thenApply:任务A执行完执行任务B,B需要A的结果,任务B有返回
public static void m3(){
// CompletableFuture.supplyAsync(()->{
// return 1;
// }).thenApply(f->{
// return f+1;
// }).thenApply(f->{
// return f+2;
// }).thenAccept(r->{
// System.out.println(r);
// });
/**
* null
*/
// System.out.println(CompletableFuture.supplyAsync(()->{
// return "resultA";
// }).thenRun(()->{
//
// }).join());
/**
* resultA
* null
*/
// System.out.println(CompletableFuture.supplyAsync(()->{
// return "resultA";
// }).thenAccept(f->{
// System.out.println(f);
// }).join());
/**
* resultA resultB
*/
// System.out.println(CompletableFuture.supplyAsync(()->{
// return "resultA";
// }).thenApply(r->{
// return r+" resultB";
// }).join());
}
- CompletableFuture与线程池
这里先说结论,下面的代码可以自己运行,看下结果是否和作者一致
* 1、没有传入自定义线程池,都用默认线程池ForkJoinPool
* 2、传入了自定义线程池:执行第一个任务时传入自定义线程池
* -调用thenRun、thenAccept、thenApply等方法,执行剩余任务共用一个线程池
* -调用thenRunAsync、thenAcceptAsync、thenApplyAsync等方法,第一个任务使用自定线程池,其余任务使用ForkJoin线程池
* 3、备注:有可能处理太快,系统优化切换原则,直接使用main线程处理
public static void m4(){
/**
* 1号任务:ForkJoinPool.commonPool-worker-9
* 2号任务:ForkJoinPool.commonPool-worker-9
* 3号任务:ForkJoinPool.commonPool-worker-9
* 4号任务:ForkJoinPool.commonPool-worker-9
* 5号任务:ForkJoinPool.commonPool-worker-9
* null
*/
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// try{
// CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("1号任务:" + Thread.currentThread().getName());
// return "111";
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("2号任务:" + Thread.currentThread().getName());
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(20);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("3号任务:" + Thread.currentThread().getName());
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("4号任务:" + Thread.currentThread().getName());
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("5号任务:" + Thread.currentThread().getName());
// });
// System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
// }catch (Exception e){
// e.printStackTrace();
// }finally {
// threadPool.shutdown();
// }
/**
* 1号任务:ForkJoinPool.commonPool-worker-9
* 2号任务:ForkJoinPool.commonPool-worker-9
* 3号任务:ForkJoinPool.commonPool-worker-9
* 4号任务:ForkJoinPool.commonPool-worker-9
* 5号任务:ForkJoinPool.commonPool-worker-9
* null
*/
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// try{
// CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("1号任务:" + Thread.currentThread().getName());
// return "111";
// }).thenRunAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("2号任务:" + Thread.currentThread().getName());
// }).thenRunAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(20);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("3号任务:" + Thread.currentThread().getName());
// }).thenRunAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("4号任务:" + Thread.currentThread().getName());
// }).thenRunAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("5号任务:" + Thread.currentThread().getName());
// });
// System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
// }catch (Exception e){
// e.printStackTrace();
// }finally {
// threadPool.shutdown();
// }
/**
* 1号任务:pool-1-thread-1
* 2号任务:pool-1-thread-1
* 3号任务:pool-1-thread-1
* 4号任务:pool-1-thread-1
* 5号任务:pool-1-thread-1
* null
*/
// ExecutorService threadPool = Executors.newFixedThreadPool(5);
// try{
// CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("1号任务:" + Thread.currentThread().getName());
// return "111";
// },threadPool).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("2号任务:" + Thread.currentThread().getName());
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(20);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("3号任务:" + Thread.currentThread().getName());
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("4号任务:" + Thread.currentThread().getName());
// }).thenRun(() -> {
// try {
// TimeUnit.MILLISECONDS.sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// System.out.println("5号任务:" + Thread.currentThread().getName());
// });
// System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
// }catch (Exception e){
// e.printStackTrace();
// }finally {
// threadPool.shutdown();
// }
/**
* 1号任务:pool-1-thread-1
* 2号任务:ForkJoinPool.commonPool-worker-9
* 3号任务:ForkJoinPool.commonPool-worker-9
* 4号任务:ForkJoinPool.commonPool-worker-9
* 5号任务:ForkJoinPool.commonPool-worker-9
* null
*/
ExecutorService threadPool = Executors.newFixedThreadPool(5);
try{
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("1号任务:" + Thread.currentThread().getName());
return "111";
},threadPool).thenRunAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("2号任务:" + Thread.currentThread().getName());
}).thenRunAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("3号任务:" + Thread.currentThread().getName());
}).thenRunAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("4号任务:" + Thread.currentThread().getName());
}).thenRunAsync(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("5号任务:" + Thread.currentThread().getName());
});
System.out.println(voidCompletableFuture.get(2L,TimeUnit.SECONDS));
}catch (Exception e){
e.printStackTrace();
}finally {
threadPool.shutdown();
}
}
- 对计算速度进行选用:多个任务,看谁先执行完成
public static void m5(){
CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
System.out.println("A come in---------");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "playA";
});
CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
System.out.println("B come in---------");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "playB";
});
System.out.println(Thread.currentThread().getName()+": "+playA.applyToEither(playB, f -> {
return f + " is winner";
}).join());
}
- 对计算结果合并
public static void m6(){
CompletableFuture<Integer> integerCompletableFuture1 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----------启动");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 10;
});
CompletableFuture<Integer> integerCompletableFuture2 = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "\t -----------启动");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return 50;
});
System.out.println(integerCompletableFuture1.thenCombine(integerCompletableFuture2, (x, y) -> {
System.out.println("开始结果合并");
return x + y;
}).join());
}
四、实际应用
有一个需求,比较各大网站或者是某一网站不同商家某个产品的定价,那么我们可以想到的解决方法大致两种
- 挨个查询每家的售价
- 做成异步任务,然后汇总
我们先定义一个商家类和商家列表
class NetMall{
@Getter
private String netMallName;
public NetMall(String netMallName){
this.netMallName = netMallName;
}
public BigDecimal calculatePrice(String productName){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return BigDecimal.valueOf(ThreadLocalRandom.current().nextDouble()*2.5 + productName.charAt(2));
}
}
static List<NetMall> list = Arrays.asList(
new NetMall("jd"),
new NetMall("tb"),
new NetMall("mt")
);
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
List<String>list1 = getPrice1(list,"mysql");
for (String s : list1) {
System.out.println(s);
}
long endTime = System.currentTimeMillis();
System.out.println("----costTime:"+(endTime-startTime)+"毫秒-----");
}
然后,分别实现上述两种方式,看差距有多少
public static List<String> getPrice1(List<NetMall> list,String productName){
return list.stream().map(netMall -> String.format(productName + " in %s price is %.2f", netMall.getNetMallName(), netMall.calculatePrice(productName))).collect(Collectors.toList());
}
上面用到了链式调用,如果不熟悉的话,可以先去了解一下,这是按部就班的执行方式,看下耗时
然后我们采用异步任务处理
public static List<String> getPrice2(List<NetMall> list,String productName){
return list.stream().map(netMall ->
CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
netMall.getNetMallName(),
netMall.calculatePrice(productName))))
.collect(Collectors.toList())
.stream()
.map(s -> s.join())
.collect(Collectors.toList());
}
public static List<String> getPrice3(List<NetMall> list,String productName){
List<CompletableFuture<String>> stringList = new ArrayList<>();
List<String>result = new ArrayList<>();
ExecutorService threadPool = Executors.newFixedThreadPool(list.size());
for (NetMall netMall : list) {
// stringList.add(CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f",
// netMall.getNetMallName(),
// netMall.calculatePrice(productName))));
stringList.add(CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+" come in--------------");
return String.format(productName + " in %s price is %.2f",
netMall.getNetMallName(),
netMall.calculatePrice(productName));
},threadPool));
}
for (CompletableFuture<String> stringCompletableFuture : stringList) {
result.add(stringCompletableFuture.join());
}
threadPool.shutdown();
return result;
}
这里给出了链式调用和普通写法,可以先看普通写法理解从哪里转换到哪里,再去看链式调用,就比较清晰了
可以看到,耗时差距耗时很大的,如果要执行的任务很多,异步任务的优势就很大了。