CompletableFuture是Java8新加入的异步工具类,扩展了Future,吸纳了一些第三方类库的特性。
来看一下这个类的方法列表:
是不是看着就很强大,嗯 ,让我们开始。。
等等,异步工具是个啥?
他可以说就是你雇来的特工,把要交付的东西交代清楚了以后,只需在你方便的时候等他给你交货,也可以直接告诉货么了怎么处理。在这期间,你自己想干啥干啥,特工自己会去完成任务。
嗯,可以开始了。。
创建一个CompletableFuture
还可以用静态方法来创建,让他给我搞到10W刀:
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "$100,000");
这里用的是supplyAsync(),传给它的参数是一个lambda,返回"$100,000"。
还有一个类似的静态方法:
CompletableFuture<Void> future = CompletableFuture
.runAsync(() -> System.out.println("mission complet!"));
runAsync()与supplyAsync()的区别在于他不能提供返回值。范型为Void。
这里还有另一个静态方法:
CompletableFuture<Integer> future = CompletableFuture
.completedFuture(100);
这里以创建一个直接返回结果的future。这有啥用呢。往后看。
安排工作
对于创建时没有安排工作的特工,还要有一步布置任务:
CompletableFuture<Integer> future = CompletableFuture
.completedFuture(100);
future.handle((i, ex) -> 100 * i);
handle()方法,给特工安排任务。参数类型为BiFunction<? super T, Throwable, ? extends U> fn。
这里直接返回了i的100倍。这个i,就是前面completedFuture(100)里的100。
也就是说,handle是拿之前future的执行结果和异常信息交给下一个特工继续进行任务。
注意,这里是指的下一个特工,也就是这原来的特工还是只返回了100,乘100倍的事是另一个人干的。也就是handle()方法的返回值。正确的打开方式是这样的:
CompletableFuture<Integer> future = CompletableFuture
.completedFuture(100);
CompletableFuture<Integer> future2 = future.handle((i, ex) -> 100 * i);
去干活吧
有了已经安排好工作的特工,让我们看看怎么让他们开始干活,啥?他们在到任务的时候已经出发了?
看下面这个代码:
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
String money = "$0";
for (int i = 10000; i <= 100000; i += 10000) {
try {
Thread.sleep(1000);
} catch (Exception e) {
}
money = "$" + i;
System.out.println("得到钱:" + money);
}
return money;
});
IntStream.range(1, 10).forEach((n) -> {
System.out.println("到 " + n + " 号店面收帐.");
try {
Thread.sleep(1000);
} catch (Exception e) {
}
});
特工的任务去给我搞钱,他要分10次搞到10W刀,钱不是这么好搞的,所以要花点时间。
交代给他任务以后,我就去各个店面里收帐了,10个店面转一圈,也是要花点时间的。
看输出结果:
到 1 号店面收帐.
得到钱:$10000
到 2 号店面收帐.
得到钱:$20000
到 3 号店面收帐.
得到钱:$30000
到 4 号店面收帐.
得到钱:$40000
到 5 号店面收帐.
得到钱:$50000
到 6 号店面收帐.
得到钱:$60000
到 7 号店面收帐.
得到钱:$70000
到 8 号店面收帐.
得到钱:$80000
到 9 号店面收帐.
得到钱:$90000
是并发的,也就是说,我收帐的同时特工也在给我去搞钱。
想想就开心。
那特工搞到的钱。怎么给我呢??
获取结果
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "$100,000");
String getString = future.get();
System.out.println(getString);
最直接的方式就是和Future一样,用get()方法,从Future里货取返回值。10W刀到手了。
CompletableFuture还有一个独有的方法:join(),和get()的功能类似。与是从Future里货取返回值。
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "$100,000");
String getString = future.join();
System.out.println(getString);
区别就是,join()不会像get()抛出ExecutionException,那特工任务在执行过程中出现问题怎么处理?这个后面会讲。
嗯,我们现在把另一个特工也让他去干活吧。
CompletableFuture<Integer> future = CompletableFuture
.completedFuture(100);
CompletableFuture<Integer> future2 = future.handle((i, ex) -> 100 * i);
Integer getInteger = future2.join();
System.out.println(getInteger);
这里得到了10000,如果调用future.join()呢?只有100。
想要的结果有了,还有个问题,现在用join()的方式 ,是和特工约好在我收完账以后见面,不见不散的那种,同步死等。
比如把前面那个收钱的代码改一下。等从特工处收到钱以后,我还要再去别的店里收账。
CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> {
int money = 0;
for (money = 10000; money <= 100000; money += 10000) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
System.out.println("特工得到钱:$" + money);
}
return money;
});
int myMoney = 0;
int n = 0;
for (n = 1; n <= 10; n++) {
myMoney += 1000;
System.out.println("到 " + n + " 号店面收帐,我得到钱:$" + myMoney);
try {
Thread.sleep(1000);
} catch (Exception e) {
}
}
int getMoney = future.join();
System.out.println("我从特工处收获:" + getMoney);
System.out.println("一起存入银行:" + (getMoney + myMoney));
System.out.println("继续收线");
myMoney = 0;
for (; n <= 20; n++) {
myMoney += 1000;
System.out.println("到 " + n + " 号店面收帐,我得到钱:$" + myMoney);
try {
Thread.sleep(1000);
} catch (Exception e) {
}
}
输出如下:
到 1 号店面收帐,我得到钱:$1000
到 2 号店面收帐,我得到钱:$2000
特工得到钱:$10000
到 3 号店面收帐,我得到钱:$3000
到 4 号店面收帐,我得到钱:$4000
特工得到钱:$20000
到 5 号店面收帐,我得到钱:$5000
到 6 号店面收帐,我得到钱:$6000
特工得到钱:$30000
到 7 号店面收帐,我得到钱:$7000
到 8 号店面收帐,我得到钱:$8000
特工得到钱:$40000
到 9 号店面收帐,我得到钱:$9000
到 10 号店面收帐,我得到钱:$10000
特工得到钱:$50000
特工得到钱:$60000
特工得到钱:$70000
特工得到钱:$80000
特工得到钱:$90000
特工得到钱:$100000
我从特工处收获:110000
一起存入银行:120000
继续收线
到 11 号店面收帐,我得到钱:$1000
到 12 号店面收帐,我得到钱:$2000
到 13 号店面收帐,我得到钱:$3000
到 14 号店面收帐,我得到钱:$4000
到 15 号店面收帐,我得到钱:$5000
到 16 号店面收帐,我得到钱:$6000
到 17 号店面收帐,我得到钱:$7000
到 18 号店面收帐,我得到钱:$8000
到 19 号店面收帐,我得到钱:$9000
到 20 号店面收帐,我得到钱:$10000
能看出,由于特工的速度慢,我要等他交付以后才能继续收账。
如果见面后的工作是很简单的事,能不能交代给特工做,我就不用等着了,可以继续去干别的。
CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> {
int money = 0;
for (money = 10000; money <= 100000; money += 10000) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
System.out.println("特工得到钱:$" + money);
}
return money;
});
int myMoney = 0;
int n = 0;
for (n = 1; n <= 10; n++) {
myMoney += 1000;
System.out.println("到 " + n + " 号店面收帐,我得到钱:$" + myMoney);
try {
Thread.sleep(1000);
} catch (Exception e) {
}
}
final int partMoney = myMoney;
future.thenAcceptAsync((money) -> {
System.out.println("我从特工处收获:" + money);
System.out.println("一起存入银行:" + (money + partMoney));
});
System.out.println("继续收线");
myMoney = 0;
for (; n <= 20; n++) {
myMoney += 1000;
System.out.println("到 " + n + " 号店面收帐,我得到钱:$" + myMoney);
try {
Thread.sleep(1000);
} catch (Exception e) {
}
}
用thenAcceptAsync()方法把完成以后的工作交付给特工,就可以继续去收账了。只不过这里的partMoney要是final的,看输出:
到 1 号店面收帐,我得到钱:$1000
到 2 号店面收帐,我得到钱:$2000
到 3 号店面收帐,我得到钱:$3000
特工得到钱:$10000
到 4 号店面收帐,我得到钱:$4000
特工得到钱:$20000
到 5 号店面收帐,我得到钱:$5000
到 6 号店面收帐,我得到钱:$6000
特工得到钱:$30000
到 7 号店面收帐,我得到钱:$7000
到 8 号店面收帐,我得到钱:$8000
特工得到钱:$40000
到 9 号店面收帐,我得到钱:$9000
到 10 号店面收帐,我得到钱:$10000
特工得到钱:$50000
继续收线
到 11 号店面收帐,我得到钱:$1000
到 12 号店面收帐,我得到钱:$2000
特工得到钱:$60000
到 13 号店面收帐,我得到钱:$3000
到 14 号店面收帐,我得到钱:$4000
特工得到钱:$70000
到 15 号店面收帐,我得到钱:$5000
到 16 号店面收帐,我得到钱:$6000
特工得到钱:$80000
到 17 号店面收帐,我得到钱:$7000
到 18 号店面收帐,我得到钱:$8000
特工得到钱:$90000
到 19 号店面收帐,我得到钱:$9000
到 20 号店面收帐,我得到钱:$10000
特工得到钱:$100000
我从特工处收获:110000
一起存入银行:120000