> 技术文档 > Java 并发编程: Runnable , CompletableFuture _completablecall

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)。