0

0

Java newSingleThreadExecutor 顺序执行与主线程同步

聖光之護

聖光之護

发布时间:2025-10-29 14:26:01

|

604人浏览过

|

来源于php中文网

原创

Java newSingleThreadExecutor 顺序执行与主线程同步

本文深入探讨了`executors.newsinglethreadexecutor()`在保证任务顺序执行方面的特性,并解释了为何在多线程环境下,主线程的输出可能与单线程执行器中任务的输出顺序不一致。核心在于主线程与执行器线程的独立性。文章提供了标准的`shutdownandawaittermination`模式,以确保主线程等待所有提交任务完成后再继续执行,从而实现预期的输出顺序和正确的资源管理。

理解 Executors.newSingleThreadExecutor() 的执行特性

Executors.newSingleThreadExecutor() 创建一个使用单个工作线程的 ExecutorService。其核心保证是:所有提交到此执行器的任务都将按照它们被提交的顺序,在一个单独的线程中依次执行。这意味着,如果你提交了任务A、B、C,那么它们一定会以A -> B -> C的顺序被该工作线程执行,绝不会并发或乱序。

然而,这种顺序保证仅限于执行器内部的任务队列。它并不意味着提交任务的主线程会等待这些任务执行完毕。主线程在提交任务后会立即继续执行其自身的代码,而执行器中的任务则在后台异步执行。这种行为可能导致主线程的输出与执行器任务的输出在控制台上的顺序出现“错位”。

考虑以下示例代码:

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

public class ZooInfo {

    public static void main(String[] args) {
        ExecutorService executorService = null;
        Runnable runnable1 = () -> System.out.println("Printing zoo inventory");
        Runnable runnable2 = () -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("Printing record " + i);
            }
        };
        try {
            executorService = Executors.newSingleThreadExecutor();
            System.out.println("Begin"); // 主线程输出
            executorService.execute(runnable1);
            executorService.execute(runnable2);
            executorService.execute(runnable1);
            System.out.println("End."); // 主线程输出
        } finally {
            if (executorService != null) {
                executorService.shutdown();
            }
        }
    }
}

运行上述代码,你可能会观察到类似以下的输出:

立即学习Java免费学习笔记(深入)”;

Begin
End.
Printing zoo inventory
Printing record 0
Printing record 1
Printing record 2
Printing zoo inventory

尽管 runnable1 和 runnable2 任务在执行器内部是按提交顺序执行的,但主线程的 System.out.println("End.") 却在这些任务的输出之前显示。这并非 newSingleThreadExecutor() 没有保证顺序,而是主线程没有等待执行器完成其工作。

花斑猪二手市场
花斑猪二手市场

本程序功能: 1 二手货品在线注册, 在线生效. 2 二手货品登陆后自动EMAIL回复功能. 3 支持货品的修改. 4 支持登记货品用户修改时密码忘记自动回复到登陆者信箱. 5 支持版主修改删除货品. 6 支持版主设置货品的点击次数, 以提高排行榜名次. 7 支持自动分页, 使用JAVA SCRPIT, 与以往程序不同, 不会因为分页过多而导致显示混乱. 8 提供点击计数功能, 同一IP点击多次无

下载

解决方案:等待执行器任务完成

为了确保主线程在所有提交的任务完成后再继续执行(例如打印“End.”),我们需要一种机制来同步主线程和执行器线程。ExecutorService 提供了 shutdown() 和 awaitTermination() 方法来实现这一目的。

  1. shutdown(): 此方法会启动一个有序的关闭过程,即不再接受新的任务,但会完成所有已提交的任务。
  2. awaitTermination(long timeout, TimeUnit unit): 此方法会阻塞当前线程,直到所有任务执行完毕、超时发生或者当前线程被中断。

将这两个方法结合起来,形成一个标准的“关闭并等待终止”模式,可以有效解决主线程与执行器线程的同步问题。

示例代码:使用 shutdownAndAwaitTermination

通常,我们会封装一个辅助方法来实现 shutdownAndAwaitTermination 逻辑:

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

public class ZooInfoCorrected {

    public static void main(String[] args) {
        ExecutorService executorService = null;
        Runnable runnable1 = () -> System.out.println("Printing zoo inventory");
        Runnable runnable2 = () -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("Printing record " + i);
            }
        };
        try {
            executorService = Executors.newSingleThreadExecutor();
            System.out.println("Begin");
            executorService.execute(runnable1);
            executorService.execute(runnable2);
            executorService.execute(runnable1);

            // 关键步骤:等待执行器任务完成
            shutdownAndAwaitTermination(executorService);

            System.out.println("End.");
        } finally {
            // 确保执行器被关闭,即使在等待过程中发生异常
            // 如果shutdownAndAwaitTermination已经调用了shutdown,这里可以省略,
            // 但为了健壮性,在finally中再次检查并调用是安全的。
            if (executorService != null && !executorService.isShutdown()) {
                executorService.shutdown();
            }
        }
    }

    /**
     * 关闭ExecutorService并等待其所有任务完成。
     * 这是一个推荐的boilerplate模式。
     *
     * @param pool 要关闭的ExecutorService
     */
    static void shutdownAndAwaitTermination(ExecutorService pool) {
        pool.shutdown(); // 禁用新任务提交
        try {
            // 等待已提交任务在指定超时时间内完成
            if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
                pool.shutdownNow(); // 如果超时,则强制关闭
                // 等待任务响应中断
                if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                    System.err.println("Pool did not terminate");
            }
        } catch (InterruptedException ie) {
            // (可选) 重新中断当前线程
            Thread.currentThread().interrupt();
            // 如果当前线程在等待时被中断,则强制关闭
            pool.shutdownNow();
            System.err.println("Current thread interrupted while waiting for pool termination.");
        }
    }
}

通过在 executorService.execute(runnable1); 之后调用 shutdownAndAwaitTermination(executorService);,主线程会阻塞,直到执行器中的所有任务完成,或者达到超时时间。这样,"End." 就会在所有任务输出之后打印:

Begin
Printing zoo inventory
Printing record 0
Printing record 1
Printing record 2
Printing zoo inventory
End.

注意事项与最佳实践

  • finally 块中的 shutdown(): 即使在 shutdownAndAwaitTermination 方法中已经调用了 shutdown(),在 main 方法的 finally 块中再次检查并调用 shutdown() 仍然是一个好的实践,以防在 try 块中其他地方发生异常导致 shutdownAndAwaitTermination 未被调用。
  • 超时处理: awaitTermination 接受一个超时参数。设定一个合理的超时时间非常重要,以防止程序无限期阻塞。如果超时发生,shutdownNow() 可以尝试中断正在执行的任务并清空任务队列。
  • InterruptedException: 在等待终止期间,如果当前线程被中断,awaitTermination 会抛出 InterruptedException。在这种情况下,通常应该重新中断当前线程(Thread.currentThread().interrupt())并清理资源,例如调用 shutdownNow()。
  • Project Loom 与结构化并发: Java 19 引入的 Project Loom (光纤) 和其孵化中的结构化并发(Structured Concurrency)特性,旨在简化并发编程,尤其是在处理一组相关任务时。结构化并发可能提供更直观的方式来确保一组任务作为一个整体完成,而无需手动管理 ExecutorService 的生命周期和等待逻辑。虽然这不直接替代 shutdownAndAwaitTermination 在现有 ExecutorService 模型中的作用,但它代表了Java并发未来发展的一个方向。

总结

Executors.newSingleThreadExecutor() 确实保证了其内部任务的顺序执行。然而,当观察到主线程的输出与执行器任务的输出顺序不符时,这通常是因为主线程没有等待执行器完成其工作。通过采用标准的 shutdownAndAwaitTermination 模式,我们可以有效地同步主线程与执行器线程,确保所有后台任务完成后,主线程再继续执行,从而实现预期的程序行为和输出顺序。正确管理 ExecutorService 的生命周期是编写健壮并发应用程序的关键。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

765

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

377

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

32

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

29

2026.01.21

C# 多线程与异步编程
C# 多线程与异步编程

本专题深入讲解 C# 中多线程与异步编程的核心概念与实战技巧,包括线程池管理、Task 类的使用、async/await 异步编程模式、并发控制与线程同步、死锁与竞态条件的解决方案。通过实际项目,帮助开发者掌握 如何在 C# 中构建高并发、低延迟的异步系统,提升应用性能和响应速度。

103

2026.02.06

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

377

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

32

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

29

2026.01.21

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

76

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.1万人学习

Java 教程
Java 教程

共578课时 | 80.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号