Java 并发编程: Runnable , CompletableFuture _completablecall
目录
一、任务抽象:Runnable 与 Callable
1. Runnable:无返回值的任务单元
2. Callable:有返回值的增强任务
二、任务执行者:线程与线程池
1. 直接使用 Thread 的问题
2. Executor 框架:线程池的核心能力
(1)关键接口与类
(2)线程池的核心参数(以 ThreadPoolExecutor 为例)
(3)常用线程池类型(通过 Executors 工具类创建)
三、异步结果管理:Future 与 CompletableFuture
1. Future:基础的异步结果容器
2. CompletableFuture:Java 8 的异步编程革命
(1)核心能力概览
(2)典型使用场景示例
四、秋招高频面试问题整理
1. 基础概念类
2. 异步编程类
3. 综合实践类
一、任务抽象:Runnable 与 Callable
1. Runnable:无返回值的任务单元
Runnable 是 Java 最基础的任务抽象接口,定义于 java.lang
包,其核心是一个无参数、无返回值且不抛异常的 run()
方法:
@FunctionalInterfacepublic interface Runnable { void run(); // 无返回值,不抛异常}
特点与用途:
适用于执行“无需关注结果”的后台任务(如日志记录、事件通知、状态更新等)。
可通过实现类、匿名内部类或 Lambda 表达式定义任务逻辑。
典型使用场景:
// 方式1:实现类class LogTask implements Runnable { @Override public void run() { System.out.println(\"记录日志:\" + System.currentTimeMillis()); }}new Thread(new LogTask()).start(); // 通过 Thread 执行// 方式2:Lambda 简化new Thread(() -> System.out.println(\"Lambda 任务\")).start();
2. Callable:有返回值的增强任务
Callable 是 Java 5 引入的接口(java.util.concurrent
包),在 Runnable 基础上增加了返回值与异常抛出能力:
@FunctionalInterfacepublic interface Callable { V call() throws Exception; // 支持返回泛型结果,可抛异常}
与 Runnable 的核心区别:
特性
Runnable
Callable
返回值
无(void)
有(泛型 V)
异常
不可抛出受检异常
可抛出任何异常(包括受检异常)
执行方式
Thread 或 Executor
需通过 ExecutorService.submit() 提交
典型使用场景:
// 定义一个返回计算结果的 CallableCallable computeTask = () -> { Thread.sleep(1000); // 模拟耗时计算 return 42; // 返回结果};// 通过线程池提交任务,获取 Future 对象ExecutorService executor = Executors.newSingleThreadExecutor();Future future = executor.submit(computeTask); // 提交 CallableInteger result = future.get(); // 阻塞获取结果(可能抛异常)System.out.println(\"计算结果:\" + result);executor.shutdown();
二、任务执行者:线程与线程池
1. 直接使用 Thread 的问题
通过继承 Thread 类或实现 Runnable 接口并调用 start()
方法,是最基础的线程创建方式。但这种方式存在显著缺陷:
- 资源开销大:每次创建线程需分配系统资源(如栈内存),频繁创建/销毁会导致性能瓶颈。
- 不可控性:无法限制并发线程数量,高并发场景下可能引发线程数爆炸(如同时创建数千个线程)。
- 缺乏管理能力:无法统一监控、复用或优雅关闭线程。
结论:生产环境应避免直接创建 Thread,优先使用线程池(Executor 框架)。
2. Executor 框架:线程池的核心能力
Java 5 引入的 java.util.concurrent
包提供了线程池标准实现,其核心组件包括:
(1)关键接口与类
组件
作用
Executor
最基础的任务执行接口,仅定义 void execute(Runnable command)
方法。
ExecutorService
扩展接口,支持提交任务(Runnable/Callable)、关闭线程池(shutdown())、获取异步结果(Future)等。
ThreadPoolExecutor
线程池的具体实现类,允许自定义核心线程数、最大线程数、任务队列、拒绝策略等参数。
Executors
工具类,提供创建线程池的快捷方法(如 newFixedThreadPool、newCachedThreadPool)。
(2)线程池的核心参数(以 ThreadPoolExecutor 为例)
public ThreadPoolExecutor( int corePoolSize, // 核心线程数(线程池长期保留的线程数量) int maximumPoolSize, // 最大线程数(线程池允许创建的最大线程数量) long keepAliveTime, // 非核心线程空闲存活时间 TimeUnit unit, // 时间单位 BlockingQueue workQueue, // 任务队列(存放待执行的 Runnable) ThreadFactory threadFactory, // 线程工厂(用于创建线程) RejectedExecutionHandler handler // 拒绝策略(当任务无法执行时的处理逻辑))
关键设计逻辑:
- 当提交任务时,线程池优先创建核心线程执行;
- 若核心线程已满,任务进入工作队列等待;
- 若队列已满且线程数未达最大值,则创建非核心线程执行;
- 若线程数已达最大值且队列与线程均满,则触发拒绝策略(如抛异常、丢弃任务等)。
(3)常用线程池类型(通过 Executors 工具类创建)
- 固定大小线程池(FixedThreadPool):核心线程数=最大线程数,无界队列(LinkedBlockingQueue),适合负载较稳定的场景。
ExecutorService fixedPool = Executors.newFixedThreadPool(4); // 4个核心线程
- 缓存线程池(CachedThreadPool):核心线程数为 0,最大线程数无限(Integer.MAX_VALUE),使用同步队列(SynchronousQueue),适合短生命周期的突发任务。
ExecutorService cachedPool = Executors.newCachedThreadPool(); // 按需创建线程
- 单线程池(SingleThreadExecutor):仅维护一个核心线程,保证任务顺序执行,适合需要严格串行的场景。
ExecutorService singlePool = Executors.newSingleThreadExecutor();
注意:生产环境中建议直接通过 ThreadPoolExecutor
构造函数自定义参数(而非依赖 Executors 的快捷方法),以避免潜在问题(如无界队列导致 OOM)。
三、异步结果管理:Future 与 CompletableFuture
1. Future:基础的异步结果容器
当通过 ExecutorService.submit(Callable)
提交任务时,会返回一个 Future
对象,用于后续获取异步任务的执行结果或状态:
Future future = executor.submit(() -> { Thread.sleep(1000); return 100;});// 阻塞获取结果(可能抛 InterruptedException 或 ExecutionException)Integer result = future.get(); // 带超时的阻塞获取Integer result = future.get(2, TimeUnit.SECONDS); // 检查任务是否完成boolean isDone = future.isDone(); // 取消任务(mayInterruptIfRunning 表示是否中断正在执行的线程)boolean isCancelled = future.cancel(true);
局限性:
- 阻塞式获取:
future.get()
会阻塞当前线程,直到任务完成(不适合高并发场景)。 - 缺乏组合能力:无法直接组合多个 Future 的结果,或实现任务依赖关系。
- 异常处理弱:需通过
ExecutionException
捕获任务内抛出的异常,代码冗余。
2. CompletableFuture:Java 8 的异步编程革命
CompletableFuture(位于 java.util.concurrent
包)是 Future 的增强版,同时实现了 Future
和 CompletionStage
接口,支持非阻塞回调、任务编排、异常处理与组合,是现代 Java 异步编程的核心工具。
(1)核心能力概览
功能
说明
异步执行
通过 runAsync(Runnable, Executor)
或 supplyAsync(Supplier, Executor)
启动异步任务。
非阻塞回调
通过 thenAccept()
、thenApply()
等方法注册任务完成后的回调逻辑。
任务组合
支持多个任务的串行(如 thenCompose
)、并行(如 allOf
/anyOf
)、组合(如 thenCombine
)。
异常处理
提供 exceptionally()
、handle()
等方法统一处理异常。
手动完成控制
支持通过 complete()
或 completeExceptionally()
手动设置结果或异常。
(2)典型使用场景示例
① 异步执行无返回值任务(替代 runAsync)
CompletableFuture future = CompletableFuture.runAsync(() -> { System.out.println(\"异步执行日志任务,线程:\" + Thread.currentThread().getName());}, executorService); // 指定自定义线程池
② 异步执行有返回值任务(替代 supplyAsync)
CompletableFuture future = CompletableFuture.supplyAsync(() -> { return \"异步计算结果\";}, executorService);// 通过 thenAccept 异步处理结果(非阻塞回调)future.thenAccept(result -> { System.out.println(\"收到结果:\" + result);});
③ 任务链式编排(串行执行)
CompletableFuture.supplyAsync(() -> { return \"Hello\"; // 第一步:生成初始数据}, executorService).thenApply(s -> s + \" CompletableFuture\") // 第二步:对第一步结果进行处理.thenAccept(System.out::println); // 第三步:消费最终结果(输出 \"Hello CompletableFuture\")
④ 组合多个异步任务
- 并行执行并合并结果(allOf):等待所有任务完成,但无返回值合并。
CompletableFuture all = CompletableFuture.allOf(future1, future2, future3);all.thenRun(() -> System.out.println(\"所有任务已完成\"));
- 合并两个任务的结果(thenCombine):两个独立任务完成后,将它们的结果合并处理。
CompletableFuture futureA = CompletableFuture.supplyAsync(() -> \"A\");CompletableFuture futureB = CompletableFuture.supplyAsync(() -> \"B\");futureA.thenCombine(futureB, (a, b) -> a + b) .thenAccept(System.out::println); // 输出 \"AB\"
⑤ 异常处理
CompletableFuture.supplyAsync(() -> { if (true) throw new RuntimeException(\"模拟异常\"); return \"正常结果\";}).exceptionally(ex -> { System.out.println(\"捕获异常:\" + ex.getMessage()); return \"默认结果\"; // 异常时返回兜底值}).thenAccept(System.out::println); // 输出 \"默认结果\"
四、秋招高频面试问题整理
1. 基础概念类
- Q1:Runnable 和 Callable 的区别是什么?
A:Runnable 无返回值且不抛异常,Callable 有返回值(泛型)且可抛异常;Callable 需通过 ExecutorService.submit() 提交并返回 Future 对象。
- Q2:线程池的核心参数有哪些?如何合理配置?
A:核心参数包括 corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(非核心线程空闲存活时间)、workQueue(任务队列)、threadFactory(线程工厂)、handler(拒绝策略)。配置需根据任务类型(CPU 密集型/IO 密集型)调整:CPU 密集型建议 corePoolSize ≈ CPU 核心数,IO 密集型可适当增大(如 2*CPU 核心数)。
- Q3:ThreadPoolExecutor 的拒绝策略有哪些?
A:默认提供 4 种:AbortPolicy(直接抛 RejectedExecutionException,默认策略)、CallerRunsPolicy(由提交任务的线程直接执行)、DiscardPolicy(静默丢弃任务)、DiscardOldestPolicy(丢弃队列中最旧的任务并重试)。
2. 异步编程类
- Q4:Future 的局限性有哪些?CompletableFuture 如何解决?
A:Future 的阻塞式获取(get())、缺乏任务组合能力、异常处理复杂。CompletableFuture 支持非阻塞回调(thenAccept/thenApply)、任务编排(thenCompose/thenCombine)、异常处理(exceptionally/handle)及手动完成控制。
- Q5:CompletableFuture.runAsync() 和 supplyAsync() 的区别?
A:runAsync() 用于提交无返回值的 Runnable 任务,返回 CompletableFuture;supplyAsync() 用于提交有返回值的 Supplier 任务,返回 CompletableFuture。两者均可指定自定义线程池(第二个参数)。
- Q6:如何实现多个异步任务的“全部完成”或“任一完成”逻辑?
A:全部完成使用 CompletableFuture.allOf(future1, future2...),任一完成使用 anyOf(future1, future2...)。注意 allOf 返回的 CompletableFuture 需通过 thenRun() 处理完成事件,anyOf 返回的 CompletableFuture 需手动判断哪个任务先完成。
3. 综合实践类
- Q7:为什么推荐使用自定义线程池而非 Executors 的快捷方法?
A:Executors 提供的快捷方法(如 newFixedThreadPool)可能使用无界队列(如 LinkedBlockingQueue),在高并发场景下易导致内存溢出(OOM);而自定义 ThreadPoolExecutor 可明确控制队列类型(如有界队列 ArrayBlockingQueue)及拒绝策略,提升系统健壮性。
- Q8:异步编程中如何避免回调地狱(Callback Hell)?
A:通过 CompletableFuture 的链式调用(如 thenApply/thenAccept)将多个回调逻辑扁平化,或结合 Lambda 表达式简化代码结构;复杂场景可考虑响应式编程框架(如 Reactor、RxJava)。