Skip to content

SingleThreadPollExecutor 是什么?

约 894 字大约 3 分钟

多线程与并发京东

2025-03-25

⭐ 题目日期:

京东 - 2024/12/26

📝 题解:

SingleThreadExecutor 是 Java 中通过 Executors 工具类提供的一种单线程的线程池,底层基于 ThreadPoolExecutor 实现。它保证所有任务按提交顺序依次执行,且在任何情况下(包括线程异常退出)仅有一个工作线程存活,适合需要严格顺序执行任务的场景。


核心特性

  1. 单线程执行
    无论提交多少任务,始终只有一个工作线程处理任务,天然线程安全。
  2. 自动重建线程
    若线程因异常终止,线程池会自动创建新线程继续执行后续任务。
  3. 无界任务队列
    默认使用 LinkedBlockingQueue(容量为 Integer.MAX_VALUE),任务可无限堆积,需警惕内存溢出(OOM)。

创建方式

通过 Executors.newSingleThreadExecutor() 工厂方法创建:

ExecutorService executor = Executors.newSingleThreadExecutor();

底层实际是配置好的 ThreadPoolExecutor

new ThreadPoolExecutor(
    1,  // 核心线程数 = 1
    1,  // 最大线程数 = 1
    0L, // 空闲线程存活时间(无意义,因核心线程数=最大线程数)
    TimeUnit.MILLISECONDS,
    new LinkedBlockingQueue<>() // 无界队列
);

适用场景

  1. 任务需严格顺序执行
    例如:日志顺序写入、事件队列处理。
  2. 避免多线程竞争开销
    单线程无锁操作,简化并发控制。
  3. 替代手动管理单线程
    利用线程池的任务队列和生命周期管理功能。

示例代码

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newSingleThreadExecutor();

        // 提交10个任务
        for (int i = 0; i < 10; i++) {
            int taskId = i;
            executor.execute(() -> {
                System.out.println("任务 " + taskId + " 由线程 " + Thread.currentThread().getName() + " 执行");
                if (taskId == 5) {
                    throw new RuntimeException("模拟任务异常"); // 线程异常终止后,线程池会重建新线程
                }
            });
        }

        executor.shutdown(); // 关闭线程池
    }
}

输出

任务 0 由线程 pool-1-thread-1 执行
任务 1 由线程 pool-1-thread-1 执行
...
任务 5 由线程 pool-1-thread-1 执行
Exception in thread "pool-1-thread-1" java.lang.RuntimeException: 模拟任务异常
任务 6 由线程 pool-1-thread-2 执行  // 异常后自动重建线程
任务 7 由线程 pool-1-thread-2 执行
...

注意事项

  1. 避免无界队列内存溢出
    默认使用无界队列(LinkedBlockingQueue),若任务提交速度远快于处理速度,可能导致 OutOfMemoryError
    解决方案:自定义 ThreadPoolExecutor,改用有界队列并设置拒绝策略。

  2. 异常处理
    任务抛出的异常会导致线程终止,但线程池会自动重建线程。可通过以下方式捕获异常:

    executor.submit(() -> {
        try {
            // 任务代码
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

    或使用 Future 获取异常信息:

    Future<?> future = executor.submit(task);
    try {
        future.get();
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
  3. 关闭线程池
    使用 shutdown() 平滑关闭,或 shutdownNow() 强制终止未执行任务。


与手动创建单线程的区别

对比项SingleThreadExecutor手动创建 Thread
任务队列管理支持任务缓冲,避免资源耗尽需自行实现队列逻辑
线程重建自动重建异常终止的线程需手动捕获异常并重启线程
生命周期管理提供统一的关闭接口需手动控制线程启停

总结

SingleThreadExecutor 是一种简单但强大的线程池,适用于需要顺序执行任务且希望减少并发复杂性的场景。通过合理使用,既能简化代码,又能利用线程池的自动管理能力,避免手动操作单线程的繁琐和潜在风险。