什么是CompletableFuture?
用来执行异步任务的,可以被看作是多线程的一种使用方式。和ajax比较类似
API
supplyAsync
thenCompose
thenCombine
thenApply
thenApplyAsync
applyToEither
exceptionally
前置知识
四大函数式接口
-
Function函数型接口
-
Predicate断定型接口
-
Consumer消费型接口
-
Supplier供给型接口
使用场景
场景一:小白去餐厅点餐,厨师炒菜+打饭,小白打王者等待饭做好
public class _01_supplyAsync {
public static void main(String[] args) {
SmallTool.printTimeAndThread("小白进入餐厅");
SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("厨师炒菜");
SmallTool.sleepMillis(200);
SmallTool.printTimeAndThread("厨师打饭");
SmallTool.sleepMillis(100);
return "番茄炒蛋 + 米饭 做好了";
});
SmallTool.printTimeAndThread("小白在打王者");
SmallTool.printTimeAndThread(String.format("%s ,小白开吃", cf1.join()));
}
}
场景二:基于场景一,炒菜和打饭分开,厨师先炒菜,服务员盛菜打饭
public class _02_thenCompose {
public static void main(String[] args) {
SmallTool.printTimeAndThread("小白进入餐厅");
SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("厨师炒菜");
SmallTool.sleepMillis(200);
return "番茄炒蛋";
}).thenCompose(dish -> CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员打饭");
SmallTool.sleepMillis(100);
return dish + " + 米饭";
}));
SmallTool.printTimeAndThread("小白在打王者");
SmallTool.printTimeAndThread(String.format("%s 好了,小白开吃", cf1.join()));
}
/**
* 用 applyAsync 也能实现
*/
private static void applyAsync() {
SmallTool.printTimeAndThread("小白进入餐厅");
SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("厨师炒菜");
SmallTool.sleepMillis(200);
CompletableFuture<String> race = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员打饭");
SmallTool.sleepMillis(100);
return " + 米饭";
});
return "番茄炒蛋" + race.join();
});
SmallTool.printTimeAndThread("小白在打王者");
SmallTool.printTimeAndThread(String.format("%s 好了,小白开吃", cf1.join()));
}
}
场景三:基于场景二,服务员要先蒸饭(同步炒菜进行)才能打饭
public class _03_thenCombine {
public static void main(String[] args) {
SmallTool.printTimeAndThread("小白进入餐厅");
SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("厨师炒菜");
SmallTool.sleepMillis(200);
return "番茄炒蛋";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员蒸饭");
SmallTool.sleepMillis(300);
return "米饭";
}), (dish, rice) -> {
SmallTool.printTimeAndThread("服务员打饭");
SmallTool.sleepMillis(100);
return String.format("%s + %s 好了", dish, rice);
});
SmallTool.printTimeAndThread("小白在打王者");
SmallTool.printTimeAndThread(String.format("%s ,小白开吃", cf1.join()));
}
/**
* 用 applyAsync 也能实现
*/
private static void applyAsync() {
SmallTool.printTimeAndThread("小白进入餐厅");
SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("厨师炒菜");
SmallTool.sleepMillis(200);
return "番茄炒蛋";
});
CompletableFuture<String> race = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员蒸饭");
SmallTool.sleepMillis(300);
return "米饭";
});
SmallTool.printTimeAndThread("小白在打王者");
String result = String.format("%s + %s 好了", cf1.join(), race.join());
SmallTool.printTimeAndThread("服务员打饭");
SmallTool.sleepMillis(100);
SmallTool.printTimeAndThread(String.format("%s ,小白开吃", result));
}
}
场景四:小白结账,打电话。服务员结账开发票。
public class _01_thenApply {
public static void main(String[] args) {
SmallTool.printTimeAndThread("小白吃好了");
SmallTool.printTimeAndThread("小白 结账、要求开发票");
CompletableFuture<String> invoice = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员收款 500元");
SmallTool.sleepMillis(100);
return "500";
}).thenApply(money -> {
SmallTool.printTimeAndThread(String.format("服务员开发票 面额 %s元", money));
SmallTool.sleepMillis(200);
return String.format("%s元发票", money);
});
SmallTool.printTimeAndThread("小白 接到朋友的电话,想一起打游戏");
SmallTool.printTimeAndThread(String.format("小白拿到%s,准备回家", invoice.join()));
}
private static void one() {
SmallTool.printTimeAndThread("小白吃好了");
SmallTool.printTimeAndThread("小白 结账、要求开发票");
CompletableFuture<String> invoice = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员收款 500元");
SmallTool.sleepMillis(100);
SmallTool.printTimeAndThread("服务员开发票 面额 500元");
SmallTool.sleepMillis(200);
return "500元发票";
});
SmallTool.printTimeAndThread("小白 接到朋友的电话,想一起打游戏");
SmallTool.printTimeAndThread(String.format("小白拿到%s,准备回家", invoice.join()));
}
private static void two() {
SmallTool.printTimeAndThread("小白吃好了");
SmallTool.printTimeAndThread("小白 结账、要求开发票");
CompletableFuture<String> invoice = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员收款 500元");
SmallTool.sleepMillis(100);
CompletableFuture<String> waiter2 = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("服务员开发票 面额 500元");
SmallTool.sleepMillis(200);
return "500元发票";
});
return waiter2.join();
});
SmallTool.printTimeAndThread("小白 接到朋友的电话,想一起打游戏");
SmallTool.printTimeAndThread(String.format("小白拿到%s,准备回家", invoice.join()));
}
}
thenApply方法:接着上个任务执行,同一个线程执行,是任务的延申
thenApplyAsync:新的任务,可能会被分配的forkjoinpool里面新的线程执行,但是若是上一个任务执行完了,空闲的线程可能是同一个
场景五:小白等车,700路和800路同时来,哪个先到就做哪个。
public class _02_applyToEither {
public static void main(String[] args) {
SmallTool.printTimeAndThread("张三走出餐厅,来到公交站");
SmallTool.printTimeAndThread("等待 700路 或者 800路 公交到来");
CompletableFuture<String> bus = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("700路公交正在赶来");
SmallTool.sleepMillis(100);
return "700路到了";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("800路公交正在赶来");
SmallTool.sleepMillis(200);
return "800路到了";
}), firstComeBus -> {
SmallTool.printTimeAndThread(firstComeBus);
return firstComeBus;
});
SmallTool.printTimeAndThread(String.format("%s,小白坐车回家", bus.join()));
}
}
applyToEither 两个任务两个线程执行,先执行完的线程去执行新的任务。
场景六:基于场景五,出现了异常
public class _03_exceptionally {
public static void main(String[] args) {
SmallTool.printTimeAndThread("张三走出餐厅,来到公交站");
SmallTool.printTimeAndThread("等待 700路 或者 800路 公交到来");
CompletableFuture<String> bus = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("700路公交正在赶来");
SmallTool.sleepMillis(100);
int i = 5 / 0;
return "700路到了";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("800路公交正在赶来");
SmallTool.sleepMillis(200);
return "800路到了";
}), firstComeBus -> {
SmallTool.printTimeAndThread(firstComeBus);
// if (firstComeBus.startsWith("700")) {
// throw new RuntimeException("撞树了……");
// }
return firstComeBus;
}).exceptionally(e -> {
SmallTool.printTimeAndThread(e.getMessage());
SmallTool.printTimeAndThread("小白叫出租车");
return "出租车 叫到了";
});
SmallTool.printTimeAndThread(String.format("%s,小白坐车回家", bus.join()));
}
}
异常也可以再前面使用
public class _03_exceptionally {
public static void main(String[] args) {
SmallTool.printTimeAndThread("张三走出餐厅,来到公交站");
SmallTool.printTimeAndThread("等待 700路 或者 800路 公交到来");
CompletableFuture<String> bus = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("700路公交正在赶来");
SmallTool.sleepMillis(100);
try {
int i = 5 / 0;
}catch (Exception e){
throw new RuntimeException("700路公交出现故障");
}
return "700路到了";
}).exceptionally(e -> {
SmallTool.printTimeAndThread(e.getMessage());
SmallTool.printTimeAndThread("紧急维修");
return "700路重新上路";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("800路公交正在赶来");
SmallTool.sleepMillis(200);
return "800路到了";
}), firstComeBus -> {
SmallTool.printTimeAndThread(firstComeBus);
// if (firstComeBus.startsWith("700")) {
// throw new RuntimeException("撞树了……");
// }
return firstComeBus;
}).exceptionally(e -> {
SmallTool.printTimeAndThread(e.getMessage());
SmallTool.printTimeAndThread("小白叫出租车");
return "出租车 叫到了";
});
SmallTool.printTimeAndThread(String.format("%s,小白坐车回家", bus.join()));
}
}
异常处理是任务的拼接,超时依然会走800路
public class _03_exceptionally {
public static void main(String[] args) {
SmallTool.printTimeAndThread("张三走出餐厅,来到公交站");
SmallTool.printTimeAndThread("等待 700路 或者 800路 公交到来");
CompletableFuture<String> bus = CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("700路公交正在赶来");
SmallTool.sleepMillis(100);
try {
int i = 5 / 0;
}catch (Exception e){
throw new RuntimeException("700路公交出现故障");
}
return "700路到了";
}).exceptionally(e -> {
SmallTool.printTimeAndThread(e.getMessage());
SmallTool.sleepMillis(200);
SmallTool.printTimeAndThread("紧急维修");
return "700路重新上路";
}).applyToEither(CompletableFuture.supplyAsync(() -> {
SmallTool.printTimeAndThread("800路公交正在赶来");
SmallTool.sleepMillis(200);
return "800路到了";
}), firstComeBus -> {
SmallTool.printTimeAndThread(firstComeBus);
// if (firstComeBus.startsWith("700")) {
// throw new RuntimeException("撞树了……");
// }
return firstComeBus;
}).exceptionally(e -> {
SmallTool.printTimeAndThread(e.getMessage());
SmallTool.printTimeAndThread("小白叫出租车");
return "出租车 叫到了";
});
SmallTool.printTimeAndThread(String.format("%s,小白坐车回家", bus.join()));
}
}