线程池与 Future:异步任务提交与结果获取

FreeGuideOnline 最新 2026-06-17

线程池与 Future:异步任务提交与结果获取

在现代应用程序中,直接频繁地创建和销毁线程往往效率低下且难以管理。线程池提供了一种线程复用机制,而 Future 则允许我们以异步的方式获取任务执行结果。本教程将带你从基础概念入手,逐步掌握线程池的创建、任务提交以及通过 Future 获取结果的完整流程。

为什么需要线程池

  • 降低资源消耗:重复利用已创建的线程,避免频繁创建/销毁带来的开销。
  • 提高响应速度:任务到达时可直接由空闲线程执行,无需等待新线程创建。
  • 统一管理线程:线程池可以对线程的并发数量、执行策略等进行集中管控,避免无限制创建线程导致系统资源耗尽。

Java 通过 java.util.concurrent 包提供了强大的线程池支持,核心接口是 ExecutorService,常用实现类为 ThreadPoolExecutor 及其工厂方法类 Executors

创建线程池

推荐使用 ThreadPoolExecutor 构造方法或 Executors 工厂类创建线程池。对于初学者,可以从 Executors 的几个快捷方法开始,但在生产环境中建议直接使用 ThreadPoolExecutor 来明确参数。

使用 Executors 工厂方法

// 固定大小的线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(5);

// 单线程化的线程池,可保证任务顺序执行
ExecutorService singlePool = Executors.newSingleThreadExecutor();

// 可缓存的线程池,空闲线程会被回收,线程数可无限扩大
ExecutorService cachedPool = Executors.newCachedThreadPool();

使用 ThreadPoolExecutor 自定义参数

为获得更精细的控制,可以直接实例化 ThreadPoolExecutor

int corePoolSize = 5;          // 核心线程数
int maxPoolSize = 10;          // 最大线程数
long keepAliveTime = 60L;      // 空闲线程存活时间
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);

ExecutorService pool = new ThreadPoolExecutor(
    corePoolSize,
    maxPoolSize,
    keepAliveTime,
    unit,
    workQueue
);

提交任务并获得 Future

向线程池提交任务有两种主要方式:提交 RunnableCallable

  • Runnable 任务没有返回值,提交后返回的 Future<?> 仅能判断任务是否完成或取消。
  • Callable 任务可以返回计算结果,并能抛出受检异常。

提交 Runnable

Future<?> future = pool.submit(() -> {
    System.out.println("执行无返回值的任务");
});

提交 Callable

Future<String> future = pool.submit(() -> {
    // 模拟耗时操作
    Thread.sleep(1000);
    return "任务结果";
});

submit() 方法会立即返回一个 Future 对象,而任务会在线程池中的某个线程异步执行。

从 Future 获取结果

Future 接口提供了检查和等待异步执行结果的方法:

方法 说明
get() 阻塞等待直到任务完成,并获取结果。
get(long timeout, TimeUnit unit) 在指定时间内阻塞等待,超时抛出 TimeoutException
isDone() 任务是否完成(正常结束、异常、取消)均返回 true
cancel(boolean mayInterruptIfRunning) 尝试取消任务。
isCancelled() 任务是否被取消。

示例:获取结果并处理异常

Future<Integer> future = pool.submit(() -> {
    int result = 10 / 0; // 故意抛出异常
    return result;
});

try {
    Integer value = future.get(); // 这里会抛出 ExecutionException
    System.out.println("结果:" + value);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt(); // 重置中断标志
} catch (ExecutionException e) {
    Throwable cause = e.getCause(); // 获取任务中真正的异常
    System.err.println("任务执行异常:" + cause.getMessage());
}

带超时的获取

try {
    String result = future.get(2, TimeUnit.SECONDS);
    System.out.println("结果:" + result);
} catch (TimeoutException e) {
    System.out.println("任务在2秒内未完成");
    future.cancel(true); // 可尝试取消
}

Future 的局限性及 CompletableFuture 简介

Future 仅提供基本的异步等待和结果获取,不支持链式回调、组合多个异步操作等高级场景。Java 8 引入了 CompletableFuture,它实现了 FutureCompletionStage 接口,可以灵活地组合异步逻辑。

CompletableFuture.supplyAsync(() -> {
    // 异步计算
    return "Hello";
}).thenApply(result -> {
    // 处理上一步结果
    return result + " World";
}).thenAccept(System.out::println);

虽然 CompletableFuture 功能更丰富,但理解基础的线程池与 Future 机制是掌握异步编程的关键第一步。

线程池的正确关闭

线程池使用完毕后必须关闭,否则 JVM 不会退出。关闭有两种方式:

  • shutdown():平滑关闭,不再接受新任务,等待已提交任务执行完成。
  • shutdownNow():尝试立即停止所有正在执行的任务,并返回等待执行的任务列表。
pool.shutdown();
try {
    if (!pool.awaitTermination(5, TimeUnit.SECONDS)) {
        pool.shutdownNow();
    }
} catch (InterruptedException e) {
    pool.shutdownNow();
    Thread.currentThread().interrupt();
}

小结

  • 线程池通过线程复用和统一管理,大幅提升多线程编程的稳定性和效率。
  • submit() 方法接受 RunnableCallable,并返回 Future 对象。
  • Future 用于查询任务状态、取消任务和获取结果(阻塞或超时)。
  • 在复杂异步场景下,可进一步学习 CompletableFuture 以享受更灵活的编程模型。

现在你可以动手尝试编写一个简单的线程池示例,并利用 Future 异步获取计算结果了。