JAVA8新特性CompletableFuture
CompletableFuture比Future好在哪
future接口可以构建异步应用,但依然有其局限性。它很难直接表述多个Future 结果之间的依赖性。实际开发中,我们经常需要达成以下目的:
- 将两个异步计算合并为一个——这两个异步计算之间相互独立,同时第二个又依赖于第
一个的结果。 - 等待 Future 集合中的所有任务都完成。
- 仅等待 Future 集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同
一个值),并返回它的结果。 - 通过编程方式完成一个 Future 任务的执行(即以手工设定异步操作结果的方式)。
- 应对 Future 的完成事件(即当 Future 的完成事件发生时会收到通知,并能使用 Future
计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)
新的CompletableFuture将使得这些成为可能。
备注:所有以Async结尾的方法都表明该方法可以异步执行,默认在ForkJoinPool.commonPool中执行,你也可以传入一个Executor 规定方法在指定线程池中执行
CompletableFuture的静态工厂方法
方法 | 返回值 | 描述 |
runAsync(Runnable runnable) | CompletableFuture | 执行一个异步线程获取CompletableFuture,里面没有返回值,在默认线程池中执行 |
runAsync(Runnable runnable, Executor executor) | CompletableFuture | 执行一个异步线程获取CompletableFuture,里面没有返回值,在指定线程池中执行 |
supplyAsync(Supplier supplier) | CompletableFuture | 传入一个lambda表达式获取一个CompletableFuture,有返回值在默认线程池中执行 |
supplyAsync(Supplier supplier, Executor executor) | CompletableFuture | 传入一个lambda表达式获取一个CompletableFuture,有返回值在指定线程池中执行 |
/**
* @author
* @description CompletableFuture的静态工厂方法
1.runAsync(Runnable runnable)
2.runAsync(Runnable runnable, Executor executor)
3.supplyAsync(Supplier<T> supplier)
4.supplyAsync(Supplier<T> supplier, Executor executor)
* @create 2018-09-05
**/
public class Demo1 {
public static ExecutorService myExecutorService = Executors.newCachedThreadPool();
public static void main(String[] args) {
//方式1 runAsync(Runnable runnable) 异步任务提交到默认线程池
CompletableFuture future1 = CompletableFuture.runAsync(()->{
delay(2);
System.out.println("11");
});
try {
//调用get以后会在2s后打印11
future1.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//方式2 runAsync(Runnable runnable, Excutor excutor) 将异步任务提交到指定线程池
CompletableFuture future2 = CompletableFuture.runAsync(()->{
delay(2);
System.out.println("11");
}, myExecutorService);
try {
//调用get以后会在2s后打印11
future1.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//使用runAsync方法是获取不到返回值得 如果你需要获取异步线程执行的结果,就需要使用supplyAsync
//方式3 supplyAsync(Supplier<T> supplier) 异步任务提交到默认线程池
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(()->{
delay(2);
return "123";
});
try {
//调用get后可以获取异步执行的结果使用
String s = future3.get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//方式3 supplyAsync(Supplier<T> supplier) 异步任务提交到默认线程池
CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
delay(3);
return "234";
}, myExecutorService);
try {
//调用get后可以获取异步执行的结果使用
String s = future4.get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public static void delay(int a){
try {
Thread.sleep(a*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
获取CompletableFuture结果的几种方式
- 获取单个CompletableFuture的结果
方法 | 描述 |
get() | 无线等待,需要捕获异常 |
get(long timeout, TimeUnit timeunit) | 等待有限的时间,会抛出异常,超时会抛出TimeoutException |
getNow(T) | 立刻获取返回值,如果未完成返回提供的默认值 没有异常 |
join | 无限等待没有抛出异常 |
- 获取多个CompletableFuture的结果
方法 | 描述 |
allOf | 等待所有CompletableFuture完成后获取结果,但是没有返回值 |
anyOf | 只要有一个结果返回就可以获取值,有返回值 |
/**
* @author
* @description 获取结果的几种方式比较
1. get() 无线等待,需要捕获异常
2. get(long timeout, TimeUnit timeunit) 等待有限的时间,会抛出异常
3. getNow(T) 立刻获取返回值,如果未完成返回提供的默认值 没有异常
4. T join; 无限等待没有异常
多个CompletableFuture结果的获取策略
1.allOf 等待所有结果
2.anyOf 只要有一个结果返回就可以获取值
* @create 2018-09-05
**/
public class Demo2 {public static void main(String[] args) {
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
delay(2);
return "123";
});
//1. get() 无线等待,需要捕获异常
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//2. get(long timeout, TimeUnit timeunit) 等待有限的时间,会抛出异常
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(()->{
delay(2);
return "123";
});
try {
System.out.println(future2.get(1, TimeUnit.SECONDS));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
System.out.println("还没完成就获取 抛出TimeoutException");
}
//3. getNow(T) 立刻获取返回值,如果未完成返回提供的默认值 没有异常
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(()->{
delay(2);
return "123";
});
System.out.println("future3------"+future3.getNow("default"));
//4. T join; 无限等待没有异常
CompletableFuture<String> future4 = CompletableFuture.supplyAsync(()->{
delay(2);
return "123";
});
System.out.println("future4------"+future4.join());
//异常处理
CompletableFuture<String> future5 = new CompletableFuture<>();
new Thread(()->{
delay(2);
try {
throw new Exception("error le");
} catch (Exception e) {
future5.completeExceptionally(e);
}
}).start();
try {
future5.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
System.out.println("future5------get dao 异常了"+e);
}
//多个CompletableFuture的获取策略allOf, anyof
/**
* 模拟场景:获取2种商品的价格,然后获取总额
*/
//由于allof没有返回值,所以需要将allprice传入,这里为了避免线程问题用AtomicInteger
AtomicInteger allPrice = new AtomicInteger();
List<String> ids = Arrays.asList("1","2");
System.out.println("+++++");
CompletableFuture<Void> res = CompletableFuture.allOf(ids.stream().map(
id -> {
return CompletableFuture.supplyAsync(() -> {
return allPrice.addAndGet(getPriceById(id));
});
}
).toArray(CompletableFuture[]::new));
System.out.println("do something else--");
res.join();
System.out.println("_______"+allPrice);
/**
* 模拟场景:同一个产品,有两种方式获取价格的方式的,谁先返回就返回哪个结果
*
*/
CompletableFuture res2 = CompletableFuture.anyOf(ids.stream().map(id->{
return CompletableFuture.supplyAsync(()->{
return getPriceById(id);
});
}).toArray(CompletableFuture[]::new));
System.out.println("do something else 2---------");
try {
//输入的是delay时间较短的结果
System.out.println(res2.get());
} catch (Exception e) {
e.printStackTrace();
}
}
public static int getPriceById(String id){
int a = new Random().nextInt(5) ;
System.out.println("id:"+id +"____delay:"+a);
delay(a);
return a ;
}
public static void delay(int a){
try {
Thread.sleep(a*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}}
如何编排你的异步任务
下面每种方法都有对应的异步执行方法和指定线程池的方法,至于什么时候用同步什么时候用异步取决你里面执行逻辑的耗时,耗时短可以选择同步,如果耗时比较多应该使用异步
方法 | 描述 |
thenCompose | 方法允许你对两个异步操作进行流水线,第一个操作完成时,将其结果作为参数传递给第二个操作。 |
thenCombine | 两个future都完成时定义处理逻辑 |
thenAccept | 消费结果,不返回 |
thenApply | 结果转换 |
/**
* @author
* @description 处理多个CompletableFuture的方法
* thenCompose 方法允许你对两个异步操作进行流水线,第一个操作完成时,将其结果作为参数传递给第二个操作。
* thenCombine 两个future都完成时定义处理逻辑
* thenAccept 消费结果,不返回
* thenApply 将前面一部的结果进行转换
* @create 2018-09-06
**/
public class Demo3 {
public static void main(String[] args) {
/**
* 模拟调用2个服务,第一个服务返回hello,第二个返回world,最后返回hello world
*/
long t1 = System.currentTimeMillis();
CompletableFuture<String> allRes = CompletableFuture.supplyAsync(()->{
delay(1);
return "hello";
}).thenComposeAsync((res)->{
return CompletableFuture.supplyAsync(()->{
delay(2);
return res + " world ";
});
});
System.out.println("do something-----");
System.out.println("--res:"+allRes.join());
long t2 = System.currentTimeMillis();
long cost = t2 - t1 ;
System.out.println("cost : "+cost);
/**
* 模拟同时调用2个服务,第一个服务返回hello,第二个返回world,最后返回hello world,节省耗时
*/
long t3 = System.currentTimeMillis();
CompletableFuture<String> allRes2 = CompletableFuture.supplyAsync(()->{
delay(1);
return "hello";
}).thenCombine(CompletableFuture.supplyAsync(()->{
delay(2);
return "world";
}), (res1,res2)->{
return res1+" "+res2 ;
}).thenCombine(CompletableFuture.supplyAsync(()->{
delay(3);
return "Tmac";
}),(res3,res4)->{
return res3+res4 ;
});
System.out.println("do something-----");
System.out.println("--res2:"+allRes2.join());
long t4 = System.currentTimeMillis();
long cost2 = t4 - t3 ;
//由于同时发请求,这里耗时是2s
System.out.println("cost2 : "+cost2);
/**
* 拟调用2个服务,第一个服务返回hello,第二个返回world,最后只要打印出hello world就可以了
*/
CompletableFuture res3 = CompletableFuture.supplyAsync(()->{
delay(2);
return "hello";
}).thenAcceptAsync((res)->{
System.out.println( res + " world ");
});
System.out.println("do something-----");
res3.join();
/**
* 拟调用2个服务,第一个服务返回1,如果返回1调用服务二将其转换成hello world
*/
CompletableFuture res4 = CompletableFuture.supplyAsync(()->{
delay(2);
return "1";
}).thenApplyAsync((res)->{
delay(1);
String wrapper = "" ;
if("1".equals(res)){
wrapper = "hello world" ;
}
return wrapper;
});
System.out.println("do something-----");
System.out.println(res4.join());
}
public static void delay(int a){
try {
Thread.sleep(a*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}