文章目录
- Java 并发 —— CompletableFuture处理异步操作
- 简介
- CompletableFuture 四种静态方法
- 示例
- 20种CompletableFuture使用方法
- 1. 一个完整的CompletableFuture
- 2. 简单的异步执行
- 3. 基于1进行执行前修改
- 4. 前一阶段使用异步函数
- 5. 使用定制Executor在前一个阶段异步应用函数
- 6. 消费前一阶段结果
- 7. 异步消费前一阶段的结果
- 8. 异步操作的异常处理
- 9. 取消计算
- 10. 在两个完成的阶段其中之一上应用函数
- 11. 在两个完成的阶段其中之一上调用消费函数
- 12. 在两个阶段都执行完后运行一个 Runnable
- 13. 使用BiConsumer处理两个阶段的结果
- 14. 使用BiFunction处理两个阶段的结果
- 15. 异步使用BiFunction处理两个阶段的结果
- 16. 组合 CompletableFuture
- 17. 完成任意一个阶段,创建一个阶段
- 18. 所有阶段全部完成,创建新阶段
- 19. 所有阶段完成,异步创建新阶段
- 20. Action (实战)
Java 并发 —— CompletableFuture处理异步操作
简介
CompletableFuture 四种静态方法
- 四种静态方法源码
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
没有指定Executor的方法会使用ForkJoinPool.commonPool() 作为它的线程池异步代码。
示例
基础示例
使用 CompletableFuture.supplyAsync() 定义要执行的异步任务
public class ThreadTest {
public static void main(String[] args) {
/**
* CompletableFuture.supplyAsync(),定义要执行的异步任务
*/
ExecutorService executor = Executors.newFixedThreadPool(10);
CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
// TODO Auto-generated method stub
try {
System.out.println("任务1");
// 睡眠1s
TimeUnit.SECONDS.sleep(1);
System.out.println("任务2");
} catch (Exception e) {
e.printStackTrace();
}
return "返回数据async";
}
}, executor);
// return
System.out.println("任务执行完成");
executor.shutdown();
}
}
基础示例2
示例1可以做到异步执行,但我们在实际开发中,会使用到异步执行,并返回执行情况
public class ThreadTest {
public static void main(String[] args) {
/**
* 异步返回数据的处理方式
*/
ExecutorService executor = Executors.newFixedThreadPool(10);
// CompletableFuture.supplyAsync(),定义要执行的异步任务
CompletableFuture<String> asyncResult = CompletableFuture.supplyAsync(new Supplier<String>() {
@Override
public String get() {
// TODO Auto-generated method stub
try {
System.out.println("任务1");
// 睡眠1s
TimeUnit.SECONDS.sleep(1);
System.out.println("任务2");
} catch (Exception e) {
e.printStackTrace();
}
return "返回数据async";
}
}, executor);
// asyncResult.thenAccept(new Consumer<String>() , 重写accept()方法去定义回调函数
asyncResult.thenAccept(new Consumer<String>() {
@Override
public void accept(String arg0) {
System.out.println("return =" + arg0);
}
});
// return
System.out.println("任务执行完成");
executor.shutdown();
}
}
项目实战
需求
- 定义一个需要异步执行函数的Service,
在需要使用异步执行的函数上添加注释,使其支持异步执行
- 定义一个调用异步函数的Service
- 定义一个测试类,并进行测试
注意事项:
启动类要添加注释@EnableAsync,使其支持异步调用
在需要使用异步执行的函数上添加注解@Async
核心源码
- 启动类
@SpringBootApplication
@MapperScan("com.bdjr.reporting.dao.mapper")
@ComponentScan("com.bdjr.reporting")
// 使用异步调用时,要加入EnableAsync注解
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
- 供调用的异步函数的Service实现类
@Service("asynchronousService")
public class AsynchronousServiceImpl implements AsynchronousService {
@Transactional
@Override
@Async
public Future<String> asyncAfterOne(){
System.err.println("asyncAfterOne任务以执行");
return new AsyncResult<String>("asyncAfterOne执行完毕");
}
@Transactional
@Override
@Async
public Future<String> asyncAfterTwo() throws InterruptedException {
long currentTimeMillis = System.currentTimeMillis();
Thread.sleep(3000);
long currentTimeMillis1 = System.currentTimeMillis();
System.err.println("asyncAfterTwo任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
return new AsyncResult<String>("asyncAfterTwo执行完毕");
}
@Transactional
@Override
@Async
public Future<String> asyncAfterThree() throws InterruptedException {
long currentTimeMillis = System.currentTimeMillis();
Thread.sleep(6000);
long currentTimeMillis1 = System.currentTimeMillis();
System.err.println("asyncAfterThree任务耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms");
return new AsyncResult<String>("asyncAfterThree执行完毕");
}
}
- 调用异步函数的Service实现类
@Service("tuneUpService")
public class TuneUpServiceImpl implements TuneUpService {
@Autowired
private AsynchronousService asynchronousService;
@Override
public String asyncOne() throws InterruptedException {
long currentTimeMillis = System.currentTimeMillis();
asynchronousService.asyncAfterOne();
asynchronousService.asyncAfterTwo();
asynchronousService.asyncAfterThree();
long currentTimeMillis1 = System.currentTimeMillis();
return "asyncAfter任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";
}
@Override
public String asyncTwo() throws InterruptedException, ExecutionException {
long currentTimeMillis = System.currentTimeMillis();
Future<String> two = asynchronousService.asyncAfterTwo();
Future<String> three = asynchronousService.asyncAfterThree();
String result = null;
while (true) {
if(two.isDone() && three.isDone()) {
System.err.println(two.get());
System.err.println(three.get());
// 两个任务都调用完成,跳出循环
break;
}
Thread.sleep(1000);
}
long currentTimeMillis1 = System.currentTimeMillis();
result = "asyncAfter任务总耗时:"+(currentTimeMillis1-currentTimeMillis)+"ms";
return result;
}
}
单元测试
@RunWith(SpringRunner.class)
/** 标记启动类程序 **/
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableAutoConfiguration
public class TuneUpServiceImplTest {
/**
* 声明Service实现类
*/
@Autowired
private TuneUpService tuneUpService;
@Test
public void asyncOne() throws InterruptedException {
System.err.println(tuneUpService.asyncOne());
Thread.sleep(8000);
}
@Test
public void asyncTwo() throws InterruptedException, ExecutionException {
System.err.println(tuneUpService.asyncTwo());
}
}
20种CompletableFuture使用方法
1. 一个完整的CompletableFuture
- 最简单的例子,使用一个预定义的结果创建一个完整的CompletableFuture,通常我们会在计算的开始阶段使用它。
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("异步执行成功");
} else {
System.out.println("异步执行失败");
}
}
/**
* @param message 自定义信息
* @param returnMessage CompletableFuture响应信息,即我们执行字符"我是响应信息"
*/
public static void assertEquals(String message, Object returnMessage){
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
static void completedFutureExample() {
// 直接执行一个异步程序,这里打印出一句话"我是响应信息"
CompletableFuture cf = CompletableFuture.completedFuture("我是响应信息");
// 调用某个函数,校验是否成功
assertTrue(cf.isDone());
// 成功后调用校验函数
assertEquals("我定义的信息", cf.getNow(null));
}
public static void main(String[] args) {
completedFutureExample();
}
}
getNow(null)方法在future完成的情况下会返回结果,就比如上面这个例子,否则返回null (传入的参数)。
2. 简单的异步执行
- 示例说明一个简单的异步执行情况
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("True 异步执行成功");
} else {
System.out.println("True 异步执行失败");
}
}
public static void assertFalse(boolean done){
if(done) {
System.out.println("False 执行完成");
} else {
System.out.println("False 执行未失败");
}
}
public static void sleepEnough(){
try {
// 睡了2秒
TimeUnit.SECONDS.sleep(5);
System.out.println("睡了5秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 设置睡眠时间
*/
public static void randomSleep() {
try {
// 睡了3秒
TimeUnit.SECONDS.sleep(3);
System.out.println("睡了3秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
static void runAsyncExample() {
CompletableFuture cf = CompletableFuture.runAsync(() -> {
// 首次进入,通过assertTrue判断函数是否调用了
assertTrue(Thread.currentThread().isDaemon());
randomSleep();
});
// 正在执行时,还没执行完,调用assertFalse()判断执行情况为false
assertFalse(cf.isDone());
// 判断randomSleep异步函数是否在sleepEnough规定的时间内执行完毕
sleepEnough();
// 执行完了,调用assertTrue()判断异步函数randomSleep执行情况
assertTrue(cf.isDone());
}
public static void main(String[] args) {
runAsyncExample();
}
}
- CompletableFuture的方法如果以Async结尾,它会异步的执行(没有指定executor的情况下), 异步执行通过ForkJoinPool实现, 它使用守护线程去执行任务。
- 注意这是CompletableFuture的特性, 其它CompletionStage可以override这个默认的行为。
3. 基于1进行执行前修改
- 使用1的完成的CompletableFuture,返回结果为"message",通过一个函数将返回结果转换为大写。
public class FutureTest {
public static void assertFalse(boolean done){
if(done) {
System.out.println("执行完成");
} else {
System.out.println("执行未完成");
}
}
/**
* @param message 自定义信息
* @param returnMessage CompletableFuture响应信息,即我们执行字符"MESSAGE"
*/
public static void assertEquals(String message, Object returnMessage){
if(message.equals(returnMessage)) {
System.out.println("响应信息一致:" + "自定义信息:" + message + " ----- 异步函数响应信息:" + returnMessage);
} else {
System.out.println("响应信息不一致");
}
}
static void thenApplyExample() {
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApply(s -> {
assertFalse(Thread.currentThread().isDaemon());
// 将响应信息转换为大写
return s.toUpperCase();
});
assertEquals("MESSAGE", cf.getNow(null));
assertFalse(cf.isDone());
}
public static void main(String[] args) {
thenApplyExample();
}
}
- thenApply方法名称代表的行为。
- then 当前的阶段正常完成之后。本例中,当前节点完成,返回字符串message。
- Apply 返回阶段会对结果前一阶段的结果应用一个函数。
- 函数的执行会被阻塞,这意味着getNow()只有打斜操作被完成后才返回。
4. 前一阶段使用异步函数
- 通过调用异步方法(方法后边加Async后缀),串联起来的CompletableFuture可以异步地执行(使用ForkJoinPool.commonPool())。
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("True 异步执行完成");
} else {
System.out.println("True 异步执行未完成");
}
}
/**
* 设置睡眠时间
*/
public static void randomSleep() {
try {
// 睡了1秒
TimeUnit.SECONDS.sleep(1);
System.out.println("睡了1秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void assertEquals(String message, Object returnMessage){
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
public static void assertNull(Object message) {
System.out.println("异步响应信息为 :" + message);
}
static void thenApplyAsyncExample() {
// 定义异步执行行为thenApplyAsync
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
// 校验函数调用情况
assertTrue(Thread.currentThread().isDaemon());
// 先睡1秒
randomSleep();
// 将结果转换为大写
return s.toUpperCase();
});
// 输出异步函数执行后响应信息,因为睡了1秒,所以这里显示未完成
assertNull(cf.getNow(null));
// 判断响应结果是否与自定义"MESSAGE"一致
assertEquals("MESSAGE", cf.join());
// 输出异步函数执行后响应信息,这里睡眠结束后执行,所以结果是已经执行完毕了
assertNull(cf.getNow(null));
}
public static void main(String[] args) {
thenApplyAsyncExample();
}
}
5. 使用定制Executor在前一个阶段异步应用函数
- 异步方法非常重要特性就之一,提供Executor来异步地执行CompletableFuture。
- 以下例子演示如何使用固定大小线程池应用大写函数。
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("True 异步执行完成");
} else {
System.out.println("True 异步执行未完成");
}
}
public static void assertFalse(boolean done){
if(done) {
System.out.println("False 异步执行完成");
} else {
System.out.println("False 异步执行未完成");
}
}
/**
* 设置睡眠时间
*/
public static void randomSleep() {
try {
// 睡了1秒
TimeUnit.SECONDS.sleep(1);
System.out.println("睡了1秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void assertEquals(String message, Object returnMessage){
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
public static void assertNull(Object message) {
System.out.println("异步响应信息为 :" + message);
}
/**
* 定义一个线程数为3的线程池,供调用方使用
*/
static ExecutorService executor = Executors.newFixedThreadPool(3, new ThreadFactory() {
int count = 1;
@Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable, "custom-executor-" + count++);
}
});
static void thenApplyAsyncWithExecutorExample() {
// 定义一个异步执行语句"message",并使用thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)函数,注意第二个参数,使用线程池控制,意味着此异步函数有线程约束
CompletableFuture cf = CompletableFuture.completedFuture("message").thenApplyAsync(s -> {
assertTrue(Thread.currentThread().getName().startsWith("custom-executor-"));
assertFalse(Thread.currentThread().isDaemon());
randomSleep();
return s.toUpperCase();
}, executor);
assertNull(cf.getNow(null));
assertEquals("MESSAGE", cf.join());
}
public static void main(String[] args) {
thenApplyAsyncWithExecutorExample();
}
}
6. 消费前一阶段结果
- 下一阶段接收了当前阶段结果,但是计算时,不需要返回值(void),那么它就可不应用一个函数,而是一个消费者(这里指自定义StringBuffer),调用方法即变为thenAccept。
public class FutureTest {
public static void assertTrue(String message, boolean done){
if(done) {
System.out.println(message + " ---- True 异步执行完成");
} else {
System.out.println(message + " ---- True 异步执行未完成");
}
}
static void thenAcceptExample() {
StringBuilder result = new StringBuilder();
// "thenAccept message"作为函数返回值,使用void thenAccept函数调用,直接将结果应用在一个自定义消费者(StringBuffer)上。
CompletableFuture.completedFuture("thenAccept message")
.thenAccept(s -> result.append(s));
// 打印函数响应信息
System.out.println("result : " + result);
// 进入assertTrue判断函数是否响应
assertTrue("Result was empty", result.length() > 0);
}
public static void main(String[] args) {
thenAcceptExample();
}
}
7. 异步消费前一阶段的结果
- 同样,可使用thenAcceptAsync方法, 串联的CompletableFuture可以异步地执行。
public class FutureTest {
public static void assertTrue(String message, boolean done){
if(done) {
System.out.println(message + " ---- True 异步执行完成");
} else {
System.out.println(message + " ---- True 异步执行未完成");
}
}
static void thenAcceptExample() {
StringBuilder result = new StringBuilder();
// 通过Async异步执行函数
CompletableFuture cf = CompletableFuture.completedFuture("thenAcceptAsync message")
.thenAcceptAsync(s -> result.append(s));
cf.join();
System.out.println("result : " + result);
assertTrue("Result was empty", result.length() > 0);
}
public static void main(String[] args) {
thenAcceptExample();
}
}
8. 异步操作的异常处理
- 异步操作如何显示异常信息
- 简单的示例,操作处理一个字符串,把它转换成答谢,我们模拟延迟一秒。
JDK9的新特性,使用CompletableFuture.delayedExecutor实现
// 后续补充
9. 取消计算
JDK9的新特性,使用CompletableFuture.delayedExecutor实现
// 后续补充
10. 在两个完成的阶段其中之一上应用函数
JDK9的新特性,使用delayedUpperCase实现
// 后续补充
11. 在两个完成的阶段其中之一上调用消费函数
JDK9的新特性,使用delayedUpperCase实现
// 后续补充
12. 在两个阶段都执行完后运行一个 Runnable
- 例子主要演示依赖CompletableFuture的两个阶段函数执行完后执行一个Runnable。
下面阶段所有都是同步执行的,第一阶段执行大写转换,第二阶段执行执行小写转换。
public class FutureTest {
public static void assertTrue(String message, boolean done){
if(done) {
System.out.println(message + " ---- True 异步执行完成");
} else {
System.out.println(message + " ---- True 异步执行未完成");
}
}
static void runAfterBothExample() {
String original = "Message";
StringBuilder result = new StringBuilder();
CompletableFuture.
// 第一段执行
completedFuture(original).
// 同步执行,将original转换为大写
thenApply(String::toUpperCase).
// 执行完后执行,第二段执行
runAfterBoth(
// 第二段执行内容
CompletableFuture.
// 依然是自定义函数
completedFuture(original).
// 同步执行,将original转换为小写
thenApply(String::toLowerCase) ,() ->
// 定义输出结果
result.append("我执行完毕了")
);
System.out.println("函数执行后的结果: " + result);
// 两阶段都执行完毕后,再执行一个自定义函数
assertTrue("Result was empty", result.length() > 0);
}
public static void main(String[] args) {
runAfterBothExample();
}
}
13. 使用BiConsumer处理两个阶段的结果
- 使用BiConsumer处理两阶段请求
public class FutureTest {
public static void assertEquals(String message, Object returnMessage){
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
static void thenAcceptBothExample() {
String original = "Message";
StringBuilder result = new StringBuilder();
CompletableFuture.
// 第一阶段执行
completedFuture(original).
// 同步执行,将original转换为大写
thenApply(String::toUpperCase).
// 多阶段都执行
thenAcceptBoth(
CompletableFuture.
// 第二阶段执行
completedFuture(original).
// 同步执行,将original转换为小写
thenApply(
String::toLowerCase),(s1, s2) ->
// 将结果拼装到result
result.append(s1 + s2)
);
assertEquals("MESSAGEmessage", result.toString());
}
public static void main(String[] args) {
thenAcceptBothExample();
}
}
14. 使用BiFunction处理两个阶段的结果
- 假设CompletableFuture依赖前两个阶段结果,它复合两个阶段的结果再返回一个结果,这种情况就可以使用thenCombine()函数。
- 此流程是同步的,getNow()会得到最终结果,它将大写和小写串联起来。
public class FutureTest {
public static void assertEquals(String message, Object returnMessage){
System.out.println("自定义值为:" + message);
System.out.println("函数返回值为:" + returnMessage);
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
public static String delayedUpperCase(String s) {
System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
return s.toUpperCase();
}
public static String delayedLowerCase(String s) {
System.out.println("转换小写 s 后的值为: " + s.toLowerCase());
return s.toLowerCase();
}
static void thenCombineExample() {
String original = "Message";
CompletableFuture cf = CompletableFuture.completedFuture(original).thenApply(s -> delayedUpperCase(s))
.thenCombine(CompletableFuture.completedFuture(original).thenApply(s -> delayedLowerCase(s)),
(s1, s2) -> s1 + s2);
assertEquals("MESSAGEmessage", cf.getNow(null));
}
public static void main(String[] args) {
thenCombineExample();
}
}
15. 异步使用BiFunction处理两个阶段的结果
类似14,不同处为:
- 依赖前两阶段,异步执行,thenCombine()也异步执行,尽管没有Async后缀。
public class FutureTest {
public static void assertEquals(String message, Object returnMessage){
System.out.println("自定义值为:" + message);
System.out.println("函数返回值为:" + returnMessage);
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
public static String delayedUpperCase(String s) {
System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
return s.toUpperCase();
}
public static String delayedLowerCase(String s) {
System.out.println("转换小写 s 后的值为: " + s.toLowerCase());
return s.toLowerCase();
}
static void thenCombineExample() {
String original = "Message";
CompletableFuture cf = CompletableFuture.completedFuture(original)
// 异步执行
.thenApplyAsync(s -> delayedUpperCase(s))
.thenCombine(CompletableFuture.completedFuture(original)
// 异步执行
.thenApplyAsync(s -> delayedLowerCase(s)),
(s1, s2) -> s1 + s2);
assertEquals("MESSAGEmessage", cf.join());
}
public static void main(String[] args) {
thenCombineExample();
}
}
16. 组合 CompletableFuture
- 使用thenCompose()完成14、15
- 此方法等待一阶段完成(转换大写),将一阶段返回的结果,通过thenCompose放入自定义变量中,再执行二阶段函数(转换小写),并将一阶段结果变量与二阶段结果拼接。
public class FutureTest {
public static void assertEquals(String message, Object returnMessage){
System.out.println("自定义值为:" + message);
System.out.println("函数返回值为:" + returnMessage);
if(message.equals(returnMessage)) {
System.out.println("响应信息一致");
} else {
System.out.println("响应信息不一致");
}
}
public static String delayedUpperCase(String s) {
System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
return s.toUpperCase();
}
public static String delayedLowerCase(String s) {
System.out.println("转换小写 s 后的值为: " + s.toLowerCase());
return s.toLowerCase();
}
static void thenComposeExample() {
String original = "Message";
CompletableFuture cf =
// 第一阶段,传入自定义original
CompletableFuture.completedFuture(original).
// 同步执行,转化大写操作
thenApply(s -> delayedUpperCase(s))
// 一阶段返回CompletableFuture(upper)向上转型了,并执行二阶段,传入自定义original,转换小写操作
.thenCompose(upper -> CompletableFuture.completedFuture(original).
// 同步执行,转换小写操作
thenApply(s -> delayedLowerCase(s))
// 一切执行完,将两次结果拼接
.thenApply(s -> upper + s));
// 判断响应结果是否一致
assertEquals("MESSAGEmessage", cf.join());
}
public static void main(String[] args) {
thenComposeExample();
}
}
17. 完成任意一个阶段,创建一个阶段
- 下面例子演示,任意一个CompletableFuture完成后,创建一个完成的CompletableFuture。
- 示例说明
- 待处理阶段首先创建,每个阶段都是转换大写操作。
- 本阶段都是同步执行(thenApply),anyOf()创建的CompletableFuture会立即执行完毕。
- 当所有阶段都完成时,使用whenComplete(BiConsumer<? super Object, ? super Throwable> action)处理完成结果。
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("全部转换为大写");
} else {
System.out.println("没有全部转换为大写");
}
}
public static void assertTrue(String message, boolean done){
if(done) {
System.out.println(message + " ---- True 异步执行完成");
} else {
System.out.println(message + " ---- True 异步执行未完成");
}
}
public static String delayedUpperCase(String s) {
System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
return s.toUpperCase();
}
public static boolean isUpperCase(String s) {
boolean flag = true;
for(int i=0; i<s.length(); i++) {
if (!Character.isUpperCase(s.charAt(i))) {
flag = false;
}
}
return flag;
}
static void anyOfExample() {
StringBuilder result = new StringBuilder();
// 定义一个List,包含3个小写字母元素
List messages = Arrays.asList("a", "b", "c");
// 迭代List,将所有值转换为大写
List<CompletableFuture> futures = (List<CompletableFuture>) messages.stream()
.map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase((String) s)))
.collect(Collectors.toList());
// 其中有一个转换了,就会马上创建一个完成的CompletableFuture,并提示,因为是同步的(thenApply),所以必定都已转换完毕
CompletableFuture.anyOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((res, th) -> {
// 迭代一阶段的结果,有一个转换成功则拼装结果,并执行最终结果
if(th == null) {
// 判断是否为大写
assertTrue(isUpperCase((String) res));
result.append(res);
}
});
System.out.println("最终结果为: " + result);
assertTrue("Result was empty", result.length() > 0);
}
public static void main(String[] args) {
anyOfExample();
}
}
18. 所有阶段全部完成,创建新阶段
- 顾名思义,17是任意一个完成,创建新阶段,这个是必须全部完成,才允许创建新阶段
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("全部转换为大写");
} else {
System.out.println("没有全部转换为大写");
}
}
public static String delayedUpperCase(String s) {
System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
return s.toUpperCase();
}
public static boolean isUpperCase(String s) {
boolean flag = true;
for(int i=0; i<s.length(); i++) {
if (!Character.isUpperCase(s.charAt(i))) {
flag = false;
}
}
return flag;
}
static void allOfExample() {
StringBuilder result = new StringBuilder();
List messages = Arrays.asList("a", "b", "c");
List<CompletableFuture> futures = (List<CompletableFuture>) messages.stream()
.map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase((String)s)))
.collect(Collectors.toList());
// 有一个执行完创建一个新阶段
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> {
// 迭代执行情况,并且所有情况都创建新阶段
futures.forEach(cf -> assertTrue(isUpperCase((String)cf.getNow(null))));
result.append("done");
});
}
public static void main(String[] args) {
allOfExample();
}
}
19. 所有阶段完成,异步创建新阶段
- 使用thenApplyAsync()替换所有单个CompletableFutures函数
- allOf()会在线程池中异步执行,所以需要调用join方法等待它完成
public class FutureTest {
public static void assertTrue(boolean done){
if(done) {
System.out.println("全部转换为大写");
} else {
System.out.println("没有全部转换为大写");
}
}
public static void assertTrue(String message, boolean done){
if(done) {
System.out.println(message + " ---- True 异步执行完成");
} else {
System.out.println(message + " ---- True 异步执行未完成");
}
}
public static String delayedUpperCase(String s) {
System.out.println("转换大写 s 后的值为: " + s.toUpperCase());
return s.toUpperCase();
}
public static boolean isUpperCase(String s) {
boolean flag = true;
for(int i=0; i<s.length(); i++) {
if (!Character.isUpperCase(s.charAt(i))) {
flag = false;
}
}
return flag;
}
static void allOfAsyncExample() {
StringBuilder result = new StringBuilder();
List messages = Arrays.asList("a", "b", "c");
List<CompletableFuture> futures = (List<CompletableFuture>) messages.stream()
// 异步执行第一阶段,使用thenApplyAsync
.map(msg -> CompletableFuture.completedFuture(msg).thenApplyAsync(s -> delayedUpperCase((String) s)))
.collect(Collectors.toList());
// 当一阶段全部执行,分别创建所有新阶段
CompletableFuture allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
.whenComplete((v, th) -> {
futures.forEach(cf -> assertTrue(isUpperCase((String) cf.getNow(null))));
result.append("done");
});
allOf.join();
assertTrue("Result was empty", result.length() > 0);
}
public static void main(String[] args) {
allOfAsyncExample();
}
}
20. Action (实战)
- 1 —— 19 介绍了一些CompletionStage 和 CompletableFuture部分函数。
- 下面例子模拟真实场景,告诉你如何使用
- 首先异步调用cars方法获取Car列表,获取CompletionStage场景。cars函数远程调用一个REST API
- 然后为每个汽车添加评分,通过rating(manufacturerId)返回一个CompletionStage(阶段完成),它会异步获取汽车评分(可能又是一个新的Rest API)
- 当列表内所有汽车评分填写完,调用allOf得到最终阶段,即前面阶段全部结束后执行
- 最终阶段调用whenComplete()函数,打印每个汽车评分情况
// cars()返回一个汽车列表
cars().thenCompose(cars -> {
// 迭代所有汽车实体
List<CompletionStage> updatedCars = cars.stream()
// 为每个汽车打分
.map(car -> rating(car.manufacturerId).thenApply(r -> {
car.setRating(r);
return car;
})).collect(Collectors.toList());
// 当所有分数打完,迭代所有汽车
CompletableFuture done = CompletableFuture
.allOf(updatedCars.toArray(new CompletableFuture[updatedCars.size()]));
// 返回所有汽车得分
return done.thenApply(v -> updatedCars.stream().map(CompletionStage::toCompletableFuture)
.map(CompletableFuture::join).collect(Collectors.toList()));
}).whenComplete((cars, th) -> {
if (th == null) {
cars.forEach(System.out::println);
} else {
throw new RuntimeException(th);
}
}).toCompletableFuture().join();
每个汽车实体都是独立的,输出所有汽车评分可以异步执行,这会提高系统的性能(延迟),并且,等待所有的汽车评分被处理使用的是allOf方法,而不是手工的线程等待(Thread#join() 或 a CountDownLatch)。