CompletableFuture: 组合式异步编程
1.先了解下什么是吞吐量:
吞吐量是指对网络、设备、端口、虚电路或其他设施,单位时间内成功地传送数据的数量(以比特、字节、分组等测量)。(百科)
2.并发,并行:
并发:单个处理器核在多个任务之间切换处理
并行:多个处理器核同时处理多个任务
Future 接口
Future接口建模了一种异步计算,返回一个执行运算结果的引用,当运算结束后,这个运用hui被返回调用方。
(简单说就是你去奶茶店排队买奶茶,店员给你一个序号,你就不用在那里等待,可以去做其他事,等好了再去拿,这就是异步)
例子:
ExecutorService executor = Executors.newCachedThreadPool();
Future<Double> future = executor.submit(new Callable<Double>() {
public Double call() {
return doSomeLongComputation();
}});
doSomethingElse();
try {
Double result = future.get(2, TimeUnit.SECONDS);
} catch (Exception e) {
// 计算抛出一个异常
}
这种编程方式让你的线程可以在 ExecutorService 以并发方式调 用另一个线程执行耗时操作的同时,去执行一些其他的任务。接着,如果你已经运行到没有异步 操作的结果就无法继续任何有意义的工作时,可以调用它的get 方法去获取操作的结果。如果操 作已经完成,该方法会立刻返回操作的结果,否则它会阻塞你的线程,直到操作完成,返回相应 的结果。
Feature 使用重 载版本的get 方法,它接受一个超时的参数,通过它,你可以定义你的线程等待 Future 结果的最 长时间,而不是一直等待下去
使用 CompletableFuture 构建异步应用
我们知道 Future 接口提供了方法来检测异步计算是否已经结束(使用 isDone方法),等待异步操作结束,以及获取计算的结果。但是这些特性还不足以让你编写简洁 的并发代码。比如,我们很难表述Future 结果之间的依赖性;从文字描述上这很简单,“当长时 间计算任务完成时,请将该计算的结果通知到另一个长时间运行的计算任务,这两个计算任务都 完成后,将计算的结果与另一个查询操作结果合并”。但是,使用Future 中提供的方法完成这样 的操作又是另外一回事。这也是我们需要更具描述能力的特性的原因,比如下面这些。
将两个异步计算合并为一个 —— 这两个异步计算之间相互独立,同时第二个又依赖于第 一个的结果。
等待 Future 集合中的所有任务都完成。
仅等待 Future 集合中最快结束的任务完成(有可能因为它们试图通过不同的方式计算同 一个值),并返回它的结果。
通过编程方式完成一个 Future 任务的执行(即以手工设定异步操作结果的方式)。
应对 Future 的完成事件(即当 Future 的完成事件发生时会收到通知,并能使用 Future
计算的结果进行下一步的操作,不只是简单地阻塞等待操作的结果)。下面你会了解新的CompletableFuture 类(它实现了 Future 接口)如何利用 Java 8 的新特性以更直观的方式将上述需求都变为可能。Stream 和 CompletableFuture 的设计都遵循
了类似的模式:它们都使用了 Lambda 表达式以及流水线的思想。从这个角度,你可以说 CompletableFuture和 Future 的关系就跟 Stream 和 Collection 的关系一样。
(1)runAsync方法: 异步创建一个简单的没有返回值的Future
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println(Thread.currentThread().getName());
}, executorService);
System.out.println("主线程: " + Thread.currentThread().getName());
future.get(2, TimeUnit.SECONDS);
结果:
主线程: main
pool-1-thread-1
(2)supplyAsync方法: 异步创建一个有返回值的CompleteFuture
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
return 4;
}, executorService);
System.out.println("主线程: " + Thread.currentThread().getName());
Integer integer = future.get(2, TimeUnit.SECONDS);
System.out.println(integer);
结果:
主线程: main
pool-1-thread-1
4
(3)thenApply, thenAccept, thenRun,这三个都是同步操作
thenApply() 监听future返回,调用Future方法对返回值业务逻辑操作,这个操作有返回值,比如转换类型
thenAccept() 监听future返回,调用Consumer处理返回值,处理的结果没有返回值,比如打印结果,返回值为CompletableFuture<void>
thenRun() 监听future返回,然后自己自定义处理,返回值为CompletableFuture<void>
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
return 4;
}, executorService).thenApply(f ->{
System.out.println(Thread.currentThread().getName());
System.out.println("thenApply: + " + f);
return String.valueOf(f) + "aaa";
});
CompletableFuture<Void> voidCompletableFuture = future.thenAccept(f -> {
System.out.println(Thread.currentThread().getName());
System.out.println("thenAccept: + " + f);
});
CompletableFuture<Void> voidCompletableFuture1 = voidCompletableFuture.thenRun(() -> {
System.out.println(Thread.currentThread().getName());
System.out.println("thenRun: + ");
});
System.out.println("主线程: " + Thread.currentThread().getName());
String integer = future.get(2, TimeUnit.SECONDS);
System.out.println(integer);
pool-1-thread-1
主线程: main
pool-1-thread-1
thenApply: + 4
pool-1-thread-1
thenAccept: + 4aaa
pool-1-thread-1
thenRun: +
4aaa
(4)thenApplyAsync, thenAcceptAsync, thenRunAsync,同上面三个对应,是异步操作
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName());
return 4;
}, executorService).thenApplyAsync(f ->{
System.out.println(Thread.currentThread().getName());
System.out.println("thenApply: + " + f);
return String.valueOf(f) + "aaa";
}, executorService);
CompletableFuture<Void> voidCompletableFuture = future.thenAcceptAsync(f -> {
System.out.println(Thread.currentThread().getName());
System.out.println("thenAccept: + " + f);
}, executorService);
CompletableFuture<Void> voidCompletableFuture1 = voidCompletableFuture.thenRunAsync(() -> {
System.out.println(Thread.currentThread().getName());
System.out.println("thenRun: + ");
}, executorService);
System.out.println("主线程: " + Thread.currentThread().getName());
String integer = future.get(2, TimeUnit.SECONDS);
System.out.println(integer);
pool-1-thread-1
pool-1-thread-1
thenApply: + 4
pool-1-thread-1
thenAccept: + 4aaa
主线程: main
pool-1-thread-1
thenRun: +
4aaa
注意:这里是同一个线程,主要是因为这里线程池我用的是可缓存的线程池,线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。我们可以让线程睡眠一点时间
修改下代码:
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
return 4;
}, executorService).thenApplyAsync(f ->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println("thenApply: + " + f);
return String.valueOf(f) + "aaa";
}, executorService);
CompletableFuture<Void> voidCompletableFuture = future.thenAcceptAsync(f -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println("thenAccept: + " + f);
}, executorService);
CompletableFuture<Void> voidCompletableFuture1 = voidCompletableFuture.thenRunAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
System.out.println("thenRun: + ");
}, executorService);
System.out.println("主线程: " + Thread.currentThread().getName());
String integer = future.get(4, TimeUnit.SECONDS);
voidCompletableFuture.get();
voidCompletableFuture1.get();
System.out.println(integer);
主线程: main
pool-1-thread-1
pool-1-thread-2
thenApply: + 4
pool-1-thread-1
thenAccept: + 4aaa
pool-1-thread-2
thenRun: +
4aaa
可以看到还是存在有些线程已经完成并且被复用了,可以再试试把睡眠时间调整为不一样试试。
(3) thenCompose方法 多层结构的future返回一个结果,跟java8的flatmap差不多
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
return 4;
}, executorService).thenCompose(f -> CompletableFuture.supplyAsync(() -> f + "dddd", executorService));
System.out.println(stringCompletableFuture.get());
pool-1-thread-2
4dddd
(4)thenCombine与thenAcceptBoth, 合并两个future,thenCombine有返回值,thenAcceptBoth没有返回值
ExecutorService executorService = Executors.newCachedThreadPool();
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
return 4;
}, executorService);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
return 5;
}, executorService);
CompletableFuture<Integer> f3 = f1.thenCombine(f2, (s1, s2) -> {
System.out.println(String.format("%d,%d", s1, s2));
return s1 + s2;
});
System.out.println(f3.get());
CompletableFuture<Void> f4 = f1.thenAcceptBoth(f2, (s1, s2) -> {
System.out.println(String.format("%d,%d", s1, s2));
});
System.out.println(f4.get());
pool-1-thread-1
pool-1-thread-2
4,5
9
4,5
null
(5) allOf anyOf
allOf CompleteableFuture数组里面全部完成才返回结果
ExecutorService executorService = Executors.newCachedThreadPool();
List<CompletableFuture<String>> list = IntStream.rangeClosed(0, 10).mapToObj(i -> CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "sleep :" + i * 1000 + "s");
return i + "s";
}, executorService)).collect(Collectors.toList());
CompletableFuture<Void> f1 = CompletableFuture.allOf(list.toArray(new CompletableFuture[]{}));
System.out.println("全部已经完成了!!" + f1.get());
pool-1-thread-1sleep :0s
pool-1-thread-2sleep :1000s
pool-1-thread-3sleep :2000s
pool-1-thread-4sleep :3000s
pool-1-thread-5sleep :4000s
pool-1-thread-6sleep :5000s
pool-1-thread-7sleep :6000s
pool-1-thread-8sleep :7000s
pool-1-thread-9sleep :8000s
pool-1-thread-10sleep :9000s
pool-1-thread-11sleep :10000s
全部已经完成了!!null
anyOf CompleteableFuture数组里面其中一个完成就返回结果
ExecutorService executorService = Executors.newCachedThreadPool();
List<CompletableFuture<String>> list = IntStream.rangeClosed(0, 10).mapToObj(i -> CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(i * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "sleep :" + i * 1000 + "s");
return i + "s";
}, executorService)).collect(Collectors.toList());
CompletableFuture<Object> f1 = CompletableFuture.anyOf(list.toArray(new CompletableFuture[]{}));
System.out.println("其中一个已经完成了!!" + f1.get());
pool-1-thread-1sleep :0s
其中一个已经完成了!!0s