并行执行两个任务A和B。主线程等待时间最长为3s。所以A和B,单独运行的时长最长也是3s。
且如果A和B都没有超时,那么优先取A的值。否者谁不超时,就取谁。
线程池单个提交
比如下面的代码,先提交了两个异步任务,此时都已经开始执行了。主线程此时等待taskA任务结束。任务A用时时间为a。那么任务B此时也执行了a时间,所以taskB剩余时间为3s - a。
dart
public static void main(String[] args) {
long configTime = 3000;
ExecutorService executorService = Executors.newFixedThreadPool(5);
Future<String> taskA = executorService.submit(() -> {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "taskA";
});
Future<String> taskB = executorService.submit(() -> {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "taskB";
});
String result = null;
long timeStart = System.currentTimeMillis();
try {
result = taskA.get(configTime, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
long timeEnd = System.currentTimeMillis();
try {
result = taskB.get(configTime - (timeEnd - timeStart), TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
throw new RuntimeException(e);
}
System.out.println(result);
}
invokeAll
invoekAll 同时执行一批任务,设置整体的超时时间。
这批任务,要么执行完成,要么超时了(未完成的任务将被取消)。所以每一个任务的状态都是done。
dart
public static void main(String[] args) throws ExecutionException, InterruptedException {
long configTime = 3000;
ExecutorService executorService = Executors.newFixedThreadPool(5);
Callable<String> taskA = () -> {
int time = 0;
try {
time = new Random().nextInt(5000);
Thread.sleep(time);
// Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "taskA" + time;
};
Callable<String> taskB = () -> {
int time = 0;
try {
time = new Random().nextInt(5000);
Thread.sleep(time);
// Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "taskB " + time;
};
long start = System.currentTimeMillis();
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(taskA, taskB), configTime, TimeUnit.MILLISECONDS);
// 3. 遍历获取结果
Future<String> futureA = futures.get(0);
Future<String> futureB = futures.get(1);
long end = System.currentTimeMillis();
if (!futureA.isCancelled()) {
System.out.println(futureA.get());
} else {
System.out.println("任务A超时被取消");
}
if (!futureB.isCancelled()) {
System.out.println(futureB.get());
} else {
System.out.println("任务B超时被取消");
}
System.out.println("耗时:"+(end-start));
}
CompletableFuture
dart
public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
// 超时时间配置(和原代码一致)
long configTime = 3000;
// 保持和原代码一致的线程池配置(避免使用默认ForkJoinPool)
ExecutorService executorService = Executors.newFixedThreadPool(5);
// 定义任务A(替换原Callable)
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> {
int time = new Random().nextInt(5000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
return "任务A被中断";
}
return "taskA" + time;
}, executorService)
// 设置超时时间,超时抛出TimeoutException
.orTimeout(configTime, TimeUnit.MILLISECONDS)
// 捕获超时/异常,返回超时提示
.exceptionally(ex -> {
if (ex instanceof TimeoutException) {
return "任务A超时被取消";
} else {
return "任务A执行异常:" + ex.getMessage();
}
});
// 定义任务B(替换原Callable)
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> {
int time = new Random().nextInt(5000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
return "任务B被中断";
}
return "taskB " + time;
}, executorService)
// 设置超时时间
.orTimeout(configTime, TimeUnit.MILLISECONDS)
// 捕获超时/异常
.exceptionally(ex -> {
if (ex instanceof TimeoutException) {
return "任务B超时被取消";
} else {
return "任务B执行异常:" + ex.getMessage();
}
});
// 记录开始时间
long start = System.currentTimeMillis();
// 等待所有任务完成(无论成功/超时/异常)
CompletableFuture.allOf(futureA, futureB).join();
// 记录结束时间
long end = System.currentTimeMillis();
// 获取并输出结果
System.out.println(futureA.join());
System.out.println(futureB.join());
System.out.println("耗时:" + (end - start));
}
CompletableFuture 与传统线程池的核心区别解析
你作为Java新手,想搞清楚CompletableFuture和传统线程池(如ThreadPoolExecutor/Executors创建的线程池)的核心区别,我会从定位、用法、核心能力 三个维度,用通俗的语言+实战示例帮你理清------核心结论先抛给你:传统线程池是"执行任务的线程容器",而CompletableFuture是"基于线程池的异步编程工具",前者解决"线程管理"问题,后者解决"异步流程编排"问题。
一、核心定位与设计目标(最根本的区别)
| 特性 | 传统线程池(ThreadPoolExecutor) | CompletableFuture |
|---|---|---|
| 核心定位 | 线程的"管理者":创建、复用、销毁线程 | 异步流程的"编排者":基于线程池实现异步编程 |
| 设计目标 | 解决线程创建/销毁的开销问题,提高线程复用率 | 解决传统Future的阻塞、无法链式调用、无法组合任务的痛点 |
| 依赖关系 | 独立的基础组件 | 依赖线程池(可使用默认/commonPool或自定义线程池) |
简单说:传统线程池只负责"找个线程执行你的任务",而CompletableFuture帮你"优雅地处理任务的异步结果、组合多个异步任务"。 |
二、核心能力对比(附实战示例)
1. 结果获取方式:阻塞 vs 异步回调(最直观的区别)
传统线程池提交任务后,只能通过Future.get()阻塞式 获取结果(或轮询isDone()),主线程会被卡住;而CompletableFuture支持非阻塞的异步回调,任务完成后自动触发后续逻辑。
示例1:传统线程池的痛点(阻塞获取结果)
Java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TraditionalThreadPoolDemo {
public static void main(String[] args) throws Exception {
// 1. 创建传统线程池
ExecutorService pool = Executors.newFixedThreadPool(1);
// 2. 提交任务,获取Future
System.out.println("主线程:提交任务,开始等待结果...");
Future<String> future = pool.submit(() -> {
Thread.sleep(2000); // 模拟耗时操作
return "传统线程池的结果";
});
// 3. 必须阻塞等待结果(get()),主线程卡2秒
String result = future.get(); // 阻塞!主线程停在这里
System.out.println("主线程:获取到结果:" + result);
pool.shutdown();
}
}
执行结果 :主线程会卡在get()处,2秒后才打印结果,期间主线程无法做任何事。
示例2:CompletableFuture的异步回调(非阻塞)
Java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureDemo {
public static void main(String[] args) throws InterruptedException {
// 1. 自定义线程池(复用传统线程池)
ExecutorService pool = Executors.newFixedThreadPool(1);
// 2. 提交任务,异步回调(主线程不阻塞)
System.out.println("主线程:提交任务,继续执行其他逻辑...");
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "CompletableFuture的结果";
}, pool) // 指定自定义线程池执行
.thenAccept(result -> {
// 任务完成后自动执行(异步回调,不阻塞主线程)
System.out.println("回调线程:获取到结果:" + result);
});
// 3. 主线程不阻塞,立即执行这行代码
System.out.println("主线程:我在做其他事,比如打印日志、处理请求...");
// 等待回调执行完(仅示例用,实际业务无需手动等)
Thread.sleep(2500);
pool.shutdown();
}
}
执行结果:
Plain
主线程:提交任务,继续执行其他逻辑...
主线程:我在做其他事,比如打印日志、处理请求...
(2秒后)
回调线程:获取到结果:CompletableFuture的结果
2. 任务组合能力:无 vs 强大
传统线程池无法直接组合多个任务(比如"等3个任务都完成后汇总结果"),需要手动写逻辑判断,代码繁琐;而CompletableFuture内置allOf/anyOf等方法,轻松实现多任务组合。
示例:多任务组合(传统线程池 vs CompletableFuture)
-
传统线程池:需要手动创建多个Future,循环判断是否全部完成,代码量大且易出错;
-
CompletableFuture:一行
allOf()就能等待所有任务完成,代码简洁。
Java
// CompletableFuture组合多任务(传统线程池做不到这么优雅)
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2");
// 等待所有任务完成
CompletableFuture<Void> allTasks = CompletableFuture.allOf(task1, task2);
allTasks.thenRun(() -> {
System.out.println("所有任务完成,汇总结果:" + task1.join() + "," + task2.join());
});
3. 异常处理:繁琐 vs 优雅
-
传统线程池:任务异常只能在
get()时抛出ExecutionException,必须手动try-catch,且只能在获取结果时处理; -
CompletableFuture:内置
exceptionally()/handle()方法,能在异步流程中直接处理异常,不用等到获取结果时才处理。
Java
// CompletableFuture优雅处理异常
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("任务执行失败");
}).exceptionally(ex -> {
// 异常时返回兜底值
System.out.println("捕获异常:" + ex.getMessage());
return "兜底结果";
}).thenAccept(result -> System.out.println("最终结果:" + result)); // 输出"兜底结果"
总结(核心区别)
-
定位不同 :传统线程池是"线程管理工具",负责执行任务;
CompletableFuture是"异步编程工具",基于线程池实现异步流程编排(回调、组合、异常处理)。 -
结果处理不同 :传统线程池只能阻塞获取结果;
CompletableFuture支持非阻塞的异步回调,主线程不被卡。 -
能力边界不同 :传统线程池无任务组合、优雅异常处理能力;
CompletableFuture内置这些能力,是对传统线程池+Future的增强。
新手小贴士 :实际开发中,不会用CompletableFuture替代传统线程池,而是结合使用 ------用自定义线程池管理线程(避免默认池耗尽),用CompletableFuture编排异步任务(回调、组合)。
Java CompletableFuture 快速入门:从基础到组合
你作为Java新手,想要快速掌握CompletableFuture的核心用法,我会用最易懂的方式,结合实战代码帮你"速成"------重点聚焦日常开发中最常用的核心功能,避开复杂的底层原理,让你能马上上手。
一、先搞懂:CompletableFuture是什么?
CompletableFuture是Java 8新增的异步编程工具,解决了传统Future只能阻塞获取结果、无法链式调用的痛点。简单说:
-
它能异步执行任务,不阻塞主线程;
-
支持链式回调,任务执行完自动触发后续逻辑;
-
能组合多个异步任务(比如"同时执行多个任务,全部完成后汇总结果");
-
自带异常处理机制,不用手动try-catch。
二、核心用法(速成重点,附可运行代码)
前置说明
所有示例基于Java 8+,无需额外依赖;默认使用JDK的ForkJoinPool.commonPool()线程池(也可自定义线程池,文末会提)。
1. 基础:创建异步任务
分两种核心场景:无返回值 、有返回值。
Java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class CompletableFutureQuickStart {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// ========== 场景1:无返回值的异步任务(runAsync) ==========
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
// 异步执行的逻辑(比如耗时操作:读写文件、调用接口)
try {
Thread.sleep(1000); // 模拟耗时1秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("任务1执行完成(无返回值)");
});
// 阻塞等待任务完成(新手调试用,实际开发尽量用回调,避免阻塞)
future1.get();
// ========== 场景2:有返回值的异步任务(supplyAsync) ==========
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("任务2执行完成(有返回值)");
return "任务2的返回结果"; // 返回任意类型(String/对象/基本类型)
});
// 获取返回值(阻塞式)
String result2 = future2.get();
System.out.println("任务2返回值:" + result2);
}
}
关键解释:
-
runAsync(Runnable):适合执行"只做事、不返回结果"的异步任务,返回CompletableFuture<Void>; -
supplyAsync(Supplier<T>):适合执行"需要返回结果"的异步任务,返回CompletableFuture<T>(T是返回值类型); -
get():阻塞当前线程,直到任务完成并获取结果(新手慎用!实际优先用回调)。
2. 核心:异步回调(避免阻塞)
不用get()阻塞,而是让任务执行完自动触发后续逻辑,这是CompletableFuture的核心价值。
| 方法 | 作用 | 输入 | 输出 | 示例场景 |
|---|---|---|---|---|
thenApply |
接收上一步结果,返回新值 | 有输入 | 有输出 | 对异步结果做加工处理 |
thenApplyAsync |
接收上一步结果,返回新值 | 有输入 | 有输出 | 与thenApply不同点:重启另外一个线程执行 |
applyToEither |
接收上一步结果,返回新值 | 有输入 | 有输出 | 二者取快的那一个(和anyOf类似) |
thenAccept |
接收上一步结果,无返回值 | 有输入 | 无输出 | 消费结果(比如打印、入库) |
thenAcceptAsync |
接收上一步结果,无返回值 | 有输入 | 无输出 | 与thenAccept不同点:重启另外一个线程执行 |
acceptEither |
接收上一步结果,无返回值 | 有输入 | 无输出 | 二者取快的那一个(和anyOf类似) |
thenRun |
不接收结果,无返回值 | 无输入 | 无输出 | 结果处理完后执行收尾操作 |
Java
public class CompletableFutureCallback {
public static void main(String[] args) throws InterruptedException {
// 1. thenApply:加工结果(有输入有输出)
CompletableFuture<Integer> futureApply = CompletableFuture.supplyAsync(() -> 10)
.thenApply(num -> num * 2) // 接收10,返回20
.thenApply(num -> num + 5); // 接收20,返回25
futureApply.thenAccept(result -> System.out.println("thenApply最终结果:" + result)); // 输出25
// 2. thenAccept:消费结果(有输入无输出)
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(str -> System.out.println("thenAccept消费:" + str + " World")); // 输出Hello World
// 3. thenRun:无输入无输出
CompletableFuture.runAsync(() -> System.out.println("任务执行中..."))
.thenRun(() -> System.out.println("thenRun:任务执行完,做收尾")); // 任务结束后执行
// 等待所有回调执行完(仅示例用,实际不用手动等)
Thread.sleep(1000);
}
}
3. 必学:异常处理
异步任务抛异常时,用exceptionally或handle处理,避免程序崩溃。
Java
public class CompletableFutureException {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 场景1:exceptionally(仅处理异常,正常结果透传)
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> {
if (true) {
throw new RuntimeException("任务执行失败!");
}
return 100;
}).exceptionally(ex -> {
// 异常时返回默认值
System.out.println("捕获异常:" + ex.getMessage());
return 0; // 兜底值
});
System.out.println("exceptionally结果:" + future1.get()); // 输出0
// 场景2:handle(同时处理正常结果和异常)
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 200)
.handle((result, ex) -> {
if (ex != null) {
System.out.println("handle捕获异常:" + ex.getMessage());
return -1;
}
return result * 2; // 正常时加工结果
});
System.out.println("handle结果:" + future2.get()); // 输出400
}
}
4. 常用:任务组合
实际开发中常需要"并行执行多个任务,汇总结果",核心用allOf(全部完成)、anyOf(任意一个完成)。
Java
public class CompletableFutureCombine {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 1. allOf:等待所有任务完成(无返回值,需手动获取每个任务结果)
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "任务1结果");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "任务2结果");
CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "任务3结果");
CompletableFuture<Void> allFuture = CompletableFuture.allOf(task1, task2, task3);
allFuture.get(); // 等待所有任务完成
// 获取每个任务的结果
String result1 = task1.get();
String result2 = task2.get();
String result3 = task3.get();
System.out.println("allOf汇总结果:" + result1 + "," + result2 + "," + result3);
// 2. anyOf:任意一个任务完成就返回(返回第一个完成的结果)
CompletableFuture<String> fastTask = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(500); // 最快完成
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "快任务结果";
});
CompletableFuture<String> slowTask = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "慢任务结果";
});
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(fastTask, slowTask);
Object anyResult = anyFuture.get();
System.out.println("anyOf结果:" + anyResult); // 输出"快任务结果"
}
}
5. 进阶:自定义线程池(避免共用线程池耗尽)
默认的commonPool是所有异步任务共用的,高并发下可能耗尽,建议自定义线程池:
Java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CompletableFutureCustomPool {
// 自定义线程池(核心数=CPU核心数,新手先这么配)
private static final ExecutorService CUSTOM_POOL = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static void main(String[] args) {
// 创建任务时指定自定义线程池
CompletableFuture.supplyAsync(() -> {
System.out.println("自定义线程池执行:" + Thread.currentThread().getName());
return "自定义线程池结果";
}, CUSTOM_POOL)
.thenAccept(System.out::println);
// 程序结束前关闭线程池(实际项目中线程池通常是单例,不用手动关)
CUSTOM_POOL.shutdown();
}
}
三、总结
作为Java新手,掌握以下3个核心点就能应对80%的CompletableFuture使用场景:
-
创建任务 :
runAsync(无返回值)、supplyAsync(有返回值),优先用自定义线程池; -
回调处理 :
thenApply(加工结果)、thenAccept(消费结果),替代get()避免阻塞; -
异常与组合 :用
exceptionally兜底异常,用allOf/anyOf组合多任务。
不用死记所有API,先把上面的示例跑通、理解,遇到具体场景再查对应方法即可------这就是最适合新手的"速成"方式。