源本科技 | 码上会

Java Executor

2026/01/29
27
0

学习目标

  • 理解 Java Executor Framework 的核心作用与优势

  • 掌握 Executor、ExecutorService 与 ScheduledExecutorService 等关键接口的使用方式

  • 能够根据应用场景选择合适的线程池类型

  • 学会提交 Callable 任务并处理返回结果

  • 掌握执行器资源的正确关闭方式


什么是 Executor

Java 的 Executorjava.util.concurrent 包中的一部分,自 Java 5 引入。它提供了一套高级 API,用于管理和调度线程任务,开发者无需手动创建或控制线程,框架会自动处理任务的调度与执行。该框架的核心思想是 将任务提交与任务执行解耦,从而简化并发编程。


核心组件

Executor

Executor 是整个框架的根接口,仅定义了一个方法:

void execute(Runnable command);

它隐藏了线程创建细节。例如:

Executor executor = command -> new Thread(command).start();
executor.execute(() -> System.out.println("任务已执行"));

注意:实际开发中很少直接实现 Executor,通常使用其子接口。


ExecutorService

ExecutorService 扩展了 Executor,提供了更丰富的功能:

  • 支持 RunnableCallable 两种任务类型

  • 可以获取任务执行结果(通过 Future

  • 提供生命周期管理方法(如 shutdown()awaitTermination()

  • 支持批量任务提交(如 invokeAll()

示例:

ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(() -> System.out.println("正在执行任务"));
service.shutdown();

ScheduledExecutorAssistant

ScheduledExecutorService 继承自 ExecutorService,专用于定时或周期性任务

  • schedule(Runnable, delay, unit):延迟执行一次

  • scheduleAtFixedRate(task, initialDelay, period, unit):固定频率执行

  • scheduleWithFixedDelay(...):固定延迟执行(上一次结束到下一次开始)

示例:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> System.out.println("每5秒执行一次"), 0, 5, TimeUnit.SECONDS);

ThreadPoolExecutor

这是 ExecutorService 最常用的实现类,支持高度自定义:

  • 可配置核心线程数(corePoolSize)与最大线程数(maximumPoolSize)

  • 可指定任务队列容量

  • 支持自定义线程工厂(ThreadFactory)

  • 可设置拒绝策略(如抛异常、丢弃任务等)

虽然通常通过 Executors 工厂类创建,但理解其内部机制对调优至关重要。


AbstractExecutorService

ExecutorService 提供了默认实现(如 submit()invokeAll()),便于开发者构建自定义执行器


常见类型

Java 通过 Executors 工具类提供多种预设线程池:

线程池类型

说明

创建方式

newSingleThreadExecutor()

单线程池,任务按顺序执行

Executors.newSingleThreadExecutor()

newFixedThreadPool(n)

固定大小线程池,多余任务排队

Executors.newFixedThreadPool(4)

newCachedThreadPool()

动态线程池,适合大量短时异步任务

Executors.newCachedThreadPool()

newScheduledThreadPool(n)

支持定时 / 周期任务的线程池

Executors.newScheduledThreadPool(1)

注意:在生产环境中,建议避免直接使用 Executors 创建线程池,而应使用 ThreadPoolExecutor 显式配置参数,防止资源耗尽。


实战示例

提交 Callable 任务并获取结果

以下程序演示如何创建一个返回字符串结果的任务,并通过 Future 获取执行结果。

import java.util.concurrent.*;

class GreetingTask implements Callable<String> {
    private String name;

    public GreetingTask(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        return "你好," + name + "!";
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建任务实例
        GreetingTask task = new GreetingTask("开发者社区");

        // 创建固定大小为 4 的线程池
        ExecutorService executor = Executors.newFixedThreadPool(4);

        // 提交任务,获得 Future 对象
        Future<String> future = executor.submit(task);

        try {
            // 阻塞等待任务完成并获取结果
            String result = future.get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            System.err.println("任务执行过程中发生错误");
            e.printStackTrace();
        } finally {
            // 关闭线程池,释放资源
            executor.shutdown();
        }
    }
}

输出结果:

你好,开发者社区!

关键点说明:

  • CallableRunnable 不同,可返回结果并抛出受检异常

  • submit() 方法返回 Future<T>,用于获取结果或取消任务

  • 必须调用 shutdown() 优雅关闭线程池,否则 JVM 可能无法退出


重点总结

  • Executor Framework 将“任务”与“执行机制”分离,极大简化并发编程

  • ExecutorService 是日常开发中最常用的接口

  • Callable + Future 组合可用于获取异步计算结果

  • 不同线程池适用于不同场景:单线程保序、固定池控资源、缓存池应对突发流量

  • 务必在使用完毕后调用 shutdown(),避免线程泄漏


思考题

  1. 为什么在生产环境中不推荐直接使用 Executors.newCachedThreadPool()?可能带来什么风险?

  2. scheduleAtFixedRatescheduleWithFixedDelay 在任务执行时间超过间隔时间时,行为有何不同?

  3. 如果不调用 executorService.shutdown(),程序会有什么表现?如何验证?