executors.newsinglethreadexecutor能保证任务严格按提交顺序串行执行,因其内部封装单线程threadpoolexecutor+无界linkedblockingqueue,任务fifo排队、唯一线程逐个执行,天然避免竞态;但需注意异常处理、避免阻塞及合理关闭。

使用 Executors.newSingleThreadExecutor 是确保任务按提交顺序串行执行的最直接方式——它背后只有一个线程,天然避免了并发竞争,也无需手动加锁控制变量访问顺序。
为什么 single-thread executor 能保证顺序?
它内部封装了一个单线程的 ThreadPoolExecutor,所有任务被放入一个无界队列(默认 LinkedBlockingQueue),由唯一工作线程逐个取出、执行。这意味着:
- 任务提交顺序 = 执行开始顺序
- 前一个任务未结束,后一个任务绝不会开始
- 共享变量(如
counter、list)在任务内修改时,不存在竞态条件
典型场景:累加器与状态更新
比如需要按请求顺序更新一个全局计数器,并返回当前值:
// 安全的顺序累加(无需 synchronized 或 AtomicInteger)
ExecutorService executor = Executors.newSingleThreadExecutor();
AtomicInteger seq = new AtomicInteger(0); // 仍建议用 AtomicInteger,但非必须
List<string> log = new ArrayList(); // 可安全 add,因只在单线程中调用
executor.submit(() -> {
int current = seq.incrementAndGet();
log.add("task-1 → " + current);
});
executor.submit(() -> {
int current = seq.incrementAndGet();
log.add("task-2 → " + current);
});
// 最终 log 一定是 ["task-1 → 1", "task-2 → 2"],顺序确定、结果确定
</string>
注意事项与常见陷阱
看似简单,但实战中容易忽略关键点:
-
别在任务里阻塞线程:如调用
Thread.sleep()、同步 IO、或等待另一个未完成的 future,会卡住整个队列 - 异常要捕获并处理:未捕获的异常会导致线程终止,executor 不会自动重启线程(JDK 8+ 后虽有恢复机制,但行为不透明,建议显式 try-catch)
-
shutdown 需配合 awaitTermination:关闭前应调用
shutdown(),再用awaitTermination()等待已提交任务完成,否则可能丢任务 -
不要把它当“伪同步锁”滥用:若只是保护某个字段,用
synchronized或ReentrantLock更轻量;single-thread executor 适合逻辑上需严格串行的业务流程(如事件回放、指令流水、账务流水)
替代方案对比:什么时候不该用它?
如果只是读写一个变量,且操作极轻量(如 count++),用 AtomicInteger 更高效;如果需要异步响应但顺序无关,用 newFixedThreadPool(4) 更合理;只有当「业务语义要求绝对先后」+「任务本身含多步状态变更」时,single-thread executor 才是清晰、可维护的选择。










