pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>JavaExample</groupId>
<artifactId>JavaExample</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>23.0</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
1、JDK1.8之前的Future接口
1.1、 定义:Future用来代表异步的结果,并且提供了检查计算完成,等待完成,检索结果完成等方法,需要完成后再进行回调。
1.2、缺陷
1.2.1、将两个异步计算合成一个异步计算,这两个异步计算互相独立,同时第二个又依赖第一个的结果。
1.2.2、当Future集合中某个任务最快结束时,返回结果。
1.2.3、等待Future结合中的所有任务都完成。
1.2.4、通过编程方式完成一个Future任务的执行。
1.2.5、应对Future的完成时间。也就是我们的回调通知。
2、JDK1.8的CompletableFuture 接口
2.1、实现了CompletionStage和Future接口,扩展了Future,简化异步编程复杂性,提供了函数式编程能力,可通过回调方式处理计算结果,并且提供了转换和组合CompletableFuture的方法。
3、实践示例
总结:Runnable类型的参数会忽略计算的结果
Consumer是纯消费计算结果,BiConsumer会组合另外一个CompletionStage纯消费
Function会对计算结果做转换,BiFunction会组合另外一个CompletionStage的计算结果做转换。
package net.liuzd.java.completable.future;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.Test;
public class TestCompletableFuture {
private static Random rand = new Random();
void println(Object val) {
System.out.println(val);
}
@Test
public void testCompletedFuture() {
// completedFuture是一个静态辅助方法,用来返回一个已经计算好的CompletableFuture
CompletableFuture<String> cf = CompletableFuture.completedFuture("message");
assertTrue(cf.isDone());
// getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值
String val = cf.getNow("没返回值或者异常,在这指定返回值!");
println("1>" + val);
assertEquals("message", val);
}
@Test
public void testJoin() {
//
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
int i = 1 / 0;
return rand.nextInt();
});
// future.get();强制处理异常(throws InterruptedException, ExecutionException)
Integer val = future.join();
println("2>" + val);
}
public static CompletableFuture<Integer> compute() {
final CompletableFuture<Integer> future = new CompletableFuture<>();
// 没有关联任何的Callback、线程池、异步任务等,如果客户端调用future.get就会一致傻等下去
return future;
}
@Test
public void testComplete() {
// 声明一个异步
CompletableFuture<Integer> f = compute();
// implements Runnable
class Client extends Thread {
CompletableFuture<Integer> cf;
Client(String threadName, CompletableFuture<Integer> _cf) {
super(threadName);
cf = _cf;
}
@Override
public void run() {
try {
println(getName() + ": " + cf.get());
} catch (InterruptedException | ExecutionException e) {
println("没有获取到正确的结果,在这发送邮件通知相关人员..." + e.getMessage());
}
}
}
//
new Client("Client1", f).start();
new Client("Client2", f).start();
println("waiting ...");
// CompletableFuture.complete()、CompletableFuture.completeExceptionally只能被调用一次
// 触发完成操作,这是get的结果
f.complete(rand.nextInt());
// 可以抛出一个异常,而不是一个成功的计算结果:
// f.completeExceptionally(new Exception("不想给你结果,怎么样??"));
// 使用場景在哪 ??
// 思考下:通过一系列计算,在这给定一个返回结果,让上面二个线程得到结果后去做其它事情 ??
println("3>");
}
static int getMoreData() {
try {
// int i = 2 / 0;
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return rand.nextInt(1000);
}
@Test
public void testWhenCompleteAsync() throws InterruptedException, ExecutionException {
// 方法以runAsync表示无返回值,supplyAsync表示有返回值
// 没有指定Executor的方法会使用ForkJoinPool.commonPool()作为它的线程池执行异步代码
// 计算结果完成时的处理
// 方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(TestCompletableFuture::getMoreData);
Future<Integer> f = future.whenCompleteAsync((v, e) -> {
// 执行异步任务,得到结果后,再去其它事情
println("异步返回结果:" + v);
println("异常:" + e);
});
println("4>" + f.get());
}
@Test
public void testThenAccept() throws InterruptedException, ExecutionException {
// 纯消费(执行Action)
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 100;
});
// 返回值为NULL>参数类型是函数式接口Consumer,只有输入,没有返回值。
CompletableFuture<Void> f = future.thenAccept(v -> {
// 对结果进行消费,不做其它处理
System.out.println("总算等到你的结果了 : " + v);
});
println("5>" + f.get());
}
@Test
public void testThenAcceptBothAsync() throws InterruptedException, ExecutionException {
// thenAcceptBoth以及相关方法提供了类似的功能,当两个CompletionStage都正常完成计算的时候,就会执行提供的action
// 它用来组合另外一个异步的结果。
CompletableFuture<Integer> xFuture = CompletableFuture.supplyAsync(() -> {
return 100;
});
CompletableFuture<Integer> yFuture = CompletableFuture.supplyAsync(() -> {
return 100;
});
// 返回值为NULL>参数类型是函数式接口Consumer,只有输入,没有返回值。
xFuture.thenAcceptBothAsync(yFuture, (x, y) -> {
System.out.println("对二个结果处理:" + x * y);
});
// runAfterBoth是当两个CompletionStage都正常完成计算的时候,执行一个Runnable,这个Runnable并不使用计算的结果
xFuture.runAfterBoth(yFuture, () -> {
System.out.println("到我了...,你们的东西我没兴趣要!");
});
println("6>");
}
@Test
public void testThenRun() throws InterruptedException, ExecutionException {
// 当计算完成的时候会执行一个Runnable,与thenAccept不同,Runnable并不使用CompletableFuture计算的结果
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 100;
});
CompletableFuture<Void> f = future.thenRun(() -> {
System.out.println("你的东西,我没兴趣要,总算完成了,该我上场了...");
});
println("7>" + f.get());
}
@Test
public void testThenCompose() throws InterruptedException, ExecutionException {
// 组合新值返回
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
return 18;
});
CompletableFuture<String> f = future.thenCompose(age -> {
return CompletableFuture.supplyAsync(() -> {
return String.format("张三的年龄%d", age);
});
});
println("8>" + f.get());
}
@Test
public void testThenCombine() throws InterruptedException, ExecutionException {
// 功能更类似thenAcceptBoth,只不过thenAcceptBoth是纯消费,它的函数参数没有返回值,而thenCombine的函数参数fn有返回值
CompletableFuture<Integer> age = CompletableFuture.supplyAsync(() -> {
return 18;
});
CompletableFuture<String> name = CompletableFuture.supplyAsync(() -> {
return "张三的年龄";
});
CompletableFuture<String> result = age.thenCombine(name, (x, y) -> String.format(y + "%s", x));
println("9>" + result.get());
}
@Test
public void testAcceptEither() throws InterruptedException, ExecutionException {
// acceptEither方法是当任意一个CompletionStage完成的时候,action这个消费者就会被执行返回Void
CompletableFuture<String> name1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "张三";
});
CompletableFuture<String> name2 = CompletableFuture.supplyAsync(() -> {
return "李四";
});
CompletableFuture<Void> result = name1.acceptEither(name2, name -> {
println("谁第一个报名:" + name.toString());
});
println("10>" + result.get());
}
@Test
public void testApplyToEither() throws InterruptedException, ExecutionException {
// acceptEither当任意一个CompletionStage完成的时候,fn会被执行,它的返回值会当作新的CompletableFuture<U>的计算结果
CompletableFuture<String> name1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(10000 + rand.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "张三";
});
CompletableFuture<String> name2 = CompletableFuture.supplyAsync(() -> {
return "李四";
});
CompletableFuture<String> result = name1.applyToEither(name2, name -> {
println("到底谁第一个报名?" + name);
return name;
});
println("11>第一个报名是:" + result.get());
}
@Test
public void testAllof() throws InterruptedException, ExecutionException {
// allOf方法是当所有的CompletableFuture都执行完后执行计算
CompletableFuture<String> name = CompletableFuture.supplyAsync(() -> {
String queryName = "张三";
println("查询完成:姓名>" + queryName);
return queryName;
});
CompletableFuture<Integer> age = CompletableFuture.supplyAsync(() -> {
int queryAge = 18;
println("查询完成:年龄>" + queryAge);
return queryAge;
});
CompletableFuture<String> sex = CompletableFuture.supplyAsync(() -> {
String querySex = "男";
println("查询完成:性别>" + querySex);
return querySex;
});
CompletableFuture<Void> f = CompletableFuture.allOf(name, age, sex);
println("12>" + f.get());
}
@Test
public void testAnyOf() throws InterruptedException, ExecutionException {
// anyOf方法是当任意一个CompletableFuture执行完后就会执行计算,计算的结果相同
CompletableFuture<String> name1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(rand.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "张三";
});
CompletableFuture<String> name2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(rand.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "李四";
});
CompletableFuture<String> name3 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(rand.nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "王五";
});
// applyToEither只是判断两个CompletableFuture,anyOf返回值的计算结果是参数中其中一个CompletableFuture的计算结果
// applyToEither返回值的计算结果却是要经过fn处理的。当然还有静态方法的区别,线程池的选择等
CompletableFuture<Object> result = CompletableFuture.anyOf(name1, name2, name3);
println("13>第一个报名是:" + result.get());
}
private static ExecutorService exs = Executors.newFixedThreadPool(10);
private List<Node> getNodes(Integer parentId, String name) {
List<Node> nodes = new ArrayList<>();
int size = rand.nextInt(10);
for (int i = 0; i < size; i++) {
nodes.add(new Node(rand.nextInt(), parentId, name + rand.nextInt()));
}
return nodes;
}
@SuppressWarnings("rawtypes")
@Test
public void testAll() throws InterruptedException, ExecutionException {
// 综合运用>加载子集
// 查询父级(从缓存中获取)
CompletableFuture<List<Node>> future = CompletableFuture.supplyAsync(() -> {
return getNodes(null, "parent-");
});
Future<List<Node>> result = future.whenCompleteAsync((nodes, error) -> {
// 执行异步任务,得到结果后,加载子对象
CompletableFuture[] cfs = nodes.stream().map(node -> CompletableFuture.supplyAsync(() -> loadChild(node),
exs).whenComplete((v, childError) -> {
println("已完成加载子集:" + v);
})).toArray(CompletableFuture[]::new);
CompletableFuture.allOf(cfs).join();
});
//
List<Node> nodes = result.get();
nodes.stream().forEach(node -> {
println("result:" + node);
});
//
}
private Node loadChild(Node parent) {
parent.adds(getNodes(parent.id, "child-"));
return parent;
}
class Node {
Integer id;
Integer parentId;
String name;
List<Node> childs;
Node(Integer _id, Integer _parentId, String _name) {
id = _id;
parentId = _parentId;
name = _name;
childs = new ArrayList<>();
}
void adds(List<Node> _childs) {
childs.addAll(_childs);
}
@Override
public String toString() {
return "Node [id=" + id + ", parentId=" + parentId + ", name=" + name + ", childs=" + childs + "]";
}
}
}