Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
Java并发编程:使用ExecutorService限制线程数量的教程_创想鸟

Java并发编程:使用ExecutorService限制线程数量的教程

Java并发编程:使用ExecutorService限制线程数量的教程

本教程详细介绍了如何在java中利用`executors`框架和`executorservice`来限制并发执行的线程数量。通过将任务封装为`runnable`,并使用`executors.newfixedthreadpool()`创建固定大小的线程池,可以有效地管理资源并控制并发级别。文章还涵盖了任务提交、线程池的优雅关闭机制以及相关的最佳实践,旨在提供一个清晰、专业的并发编程指南。

引言

在Java应用程序开发中,面对需要并行处理大量任务的场景时,合理地管理并发线程至关重要。直接创建无限数量的线程可能导致系统资源耗尽、性能下降甚至程序崩溃。为了解决这一问题,Java 5引入了java.util.concurrent包,其中的Executors框架为我们提供了一套强大的工具来管理线程池,从而有效地限制和控制并发线程的数量。本教程将指导您如何使用ExecutorService来创建一个固定大小的线程池,以处理并发任务,并确保资源的有效利用。

核心概念:Executors框架与ExecutorService

Executors框架是Java并发编程的核心组件之一,它提供了一系列工厂方法来创建不同类型的ExecutorService实例。ExecutorService是一个高级接口,用于管理线程的生命周期和任务的提交。通过使用线程池,我们可以重用线程,而不是为每个任务都创建新线程,这大大降低了线程创建和销毁的开销。

为了限制并发线程的数量,最常用的方法是使用Executors.newFixedThreadPool(int nThreads)方法。这个方法会创建一个固定大小的线程池,该线程池中的线程数量始终保持不变。当有新任务提交时,如果池中所有线程都在忙碌,那么新任务将被放入一个等待队列中,直到有空闲线程可用。

定义并发任务:Runnable接口

在使用ExecutorService之前,我们需要将要并行执行的逻辑封装成一个任务。Java提供了两个核心接口来定义任务:Runnable和Callable。

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

Runnable: 适用于不需要返回结果且不抛出受检异常的任务。它只包含一个run()方法。Callable: 适用于需要返回结果且可能抛出受检异常的任务。它包含一个call()方法,并返回一个Future对象。

在本教程中,我们将以一个文件序列化任务为例,使用Runnable接口来定义任务。假设我们有一个EventuelleDestination对象列表,需要为每个对象执行序列化操作,并将结果写入文件。

首先,定义一个EventuelleDestination及其相关依赖的模拟类,以便构建完整的示例:

易优微信教育培训小程序模板 易优微信教育培训小程序模板

易优微信教育培训小程序模板是基于前端开源小程序+后端易优cms+标签化API接口,是一套开源、快速搭建个性化需求的小程序CMS。轻量级TP底层框架,前后端分离,标签化API接口可对接所有小程序,支持二次开发。即使小白用户也能轻松搭建制作一套完整的线上版小程序。 微信教育培训小程序模板主要特点:1、代码开源,支持二次修改2、微信原生写法,兼容性更好,代码可读性更强3、功能接口完整,支持eyoucms

易优微信教育培训小程序模板 0 查看详情 易优微信教育培训小程序模板

// EventuelleDestination.java - 模拟业务对象package com.example.concurrency;import java.util.Objects;public class EventuelleDestination {    private int id;    private Acceuillant eventuelAcceuillant;    public EventuelleDestination(int id, Acceuillant acceuillant) {        this.id = id;        this.eventuelAcceuillant = acceuillant;    }    public int getId() { return id; }    public Acceuillant getEventuelAcceuillant() { return eventuelAcceuillant; }    @Override    public String toString() {        return "EventuelleDestination{" + "id=" + id + ", acceuillantId=" + eventuelAcceuillant.getId() + '}';    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        EventuelleDestination that = (EventuelleDestination) o;        return id == that.id && Objects.equals(eventuelAcceuillant, that.eventuelAcceuillant);    }    @Override    public int hashCode() {        return Objects.hash(id, eventuelAcceuillant);    }}// Acceuillant.java - 模拟嵌套对象package com.example.concurrency;import java.util.Objects;public class Acceuillant {    private int id;    public Acceuillant(int id) { this.id = id; }    public int getId() { return id; }    @Override    public String toString() {        return "Acceuillant{" + "id=" + id + '}';    }    @Override    public boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Acceuillant that = (Acceuillant) o;        return id == that.id;    }    @Override    public int hashCode() {        return Objects.hash(id);    }}// EmployeDao.java - 模拟数据访问层package com.example.concurrency;public class EmployeDao {    public Employe getEmploye() { return new Employe(100); }}// Employe.java - 模拟员工对象package com.example.concurrency;public class Employe {    private int id;    public Employe(int id) { this.id = id; }    public int getId() { return id; }}// EntrepriseDao.java - 模拟数据访问层package com.example.concurrency;public class EntrepriseDao {    public int retrouveEmplacementIdParDepartementId(int departmentId) {        // 模拟耗时操作或业务逻辑        try {            Thread.sleep(5 + (int)(Math.random() * 95)); // 模拟随机耗时 5-100ms        } catch (InterruptedException e) {            Thread.currentThread().interrupt();            throw new RuntimeException("Thread interrupted during mock DAO call", e);        }        return departmentId + 500;    }}

接下来,我们将序列化逻辑封装到SerializationTask类中,它实现了Runnable接口:

// SerializationTask.java - 封装序列化任务package com.example.concurrency;import com.google.gson.Gson;import java.io.FileWriter;import java.io.IOException;import java.io.Writer;import java.nio.file.Path;import java.nio.file.Files;public class SerializationTask implements Runnable {    private final EventuelleDestination destination;    private final Path outputDirectory;    private final EmployeDao employeDao;    private final EntrepriseDao entrepriseDao;    public SerializationTask(EventuelleDestination destination, Path outputDirectory, EmployeDao employeDao, EntrepriseDao entrepriseDao) {        this.destination = destination;        this.outputDirectory = outputDirectory;        this.employeDao = employeDao;        this.entrepriseDao = entrepriseDao;    }    @Override    public void run() {        Gson gson = new Gson();        try {            // 确保输出目录存在            Files.createDirectories(outputDirectory);            String filename = employeDao.getEmploye().getId() + "_" +                              entrepriseDao.retrouveEmplacementIdParDepartementId(destination.getEventuelAcceuillant().getId()) + "_" +                              destination.getEventuelAcceuillant().getId() + ".json";            Path filePath = outputDirectory.resolve(filename);            try (Writer writer = new FileWriter(filePath.toFile())) {                gson.toJson(destination, writer);                System.out.println(Thread.currentThread().getName() + ": " + destination + " has been serialized to " + filePath);            }        } catch (IOException e) {            System.err.println(Thread.currentThread().getName() + ": Error serializing " + destination + ": " + e.getMessage());            e.printStackTrace();        } catch (RuntimeException e) { // 捕获模拟DAO中可能抛出的RuntimeException            System.err.println(Thread.currentThread().getName() + ": Runtime error during serialization of " + destination + ": " + e.getMessage());            e.printStackTrace();        }    }}

使用ExecutorService管理线程池

现在我们有了定义好的任务,接下来将使用ExecutorService来创建固定大小的线程池,并提交这些任务。

// AppExecutorDemo.java - 主应用程序package com.example.concurrency;import java.nio.file.Path;import java.nio.file.Paths;import java.time.Instant;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;import java.util.stream.IntStream;public class AppExecutorDemo {    // 定义输出目录    private final Path outputDir = Paths.get("serialized_data");    public static void main(String[] args) {        AppExecutorDemo app = new AppExecutorDemo();        app.runDemo();    }    private void runDemo() {        // 准备模拟数据和依赖        EmployeDao employeDao = new EmployeDao();        EntrepriseDao entrepriseDao = new EntrepriseDao();        // 创建20个 EventuelleDestination 对象作为任务数据        List destinations = IntStream.rangeClosed(1, 20)                                                        .mapToObj(i -> new EventuelleDestination(i, new Acceuillant(i * 10)))                                                        .toList();        // 创建一个固定大小为3的线程池        ExecutorService executorService = Executors.newFixedThreadPool(3);        System.out.println("ExecutorService created with 3 threads. Submitting tasks...");        // 提交每个序列化任务到线程池        for (EventuelleDestination dest : destinations) {            executorService.submit(new SerializationTask(dest, outputDir, employeDao, entrepriseDao));        }        System.out.println("All tasks submitted. Awaiting termination...");        // 优雅地关闭线程池        shutdownAndAwaitTermination(executorService);        System.out.println("ExecutorService terminated. All tasks completed or cancelled.");    }    /**     * 优雅地关闭ExecutorService,等待已提交任务完成。     * 此方法基于JavaDoc中ExecutorService的推荐关闭模式。     *     * @param executorService 要关闭的ExecutorService实例     */    private void shutdownAndAwaitTermination(ExecutorService executorService) {        executorService.shutdown(); // 禁用新任务提交        try {            // 等待已提交任务在指定时间内完成            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {                executorService.shutdownNow(); // 如果超时,则取消当前正在执行的任务                // 再次等待,确保任务响应中断                if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {                    System.err.println("Executor service did not terminate completely after forced shutdown. " + Instant.now());                }            }        } catch (InterruptedException ex) {            // 如果当前线程在等待期间被中断,则重新取消任务            executorService.shutdownNow();            // 重新设置中断状态            Thread.currentThread().interrupt();        }    }}

运行上述AppExecutorDemo类,您将看到类似以下的输出(具体的线程ID和时间戳会有所不同,但关键是pool-1-thread-X的数量不会超过3):

ExecutorService created with 3 threads. Submitting tasks...All tasks submitted. Awaiting termination...pool-1-thread-1: EventuelleDestination{id=1, acceuillantId=10} has been serialized to serialized_data/100_510_10.jsonpool-1-thread-2: EventuelleDestination{id=2, acceuillantId=20} has been serialized to serialized_data/100_520_20.jsonpool-1-thread-3: EventuelleDestination{id=3, acceuillantId=30} has been serialized to serialized_data/100_530_30.jsonpool-1-thread-1: EventuelleDestination{id=4, acceuillantId=40} has been serialized to serialized_data/100_540_40.jsonpool-1-thread-2: EventuelleDestination{id=5, acceuillantId=50} has been serialized to serialized_data/100_550_50.jsonpool-1-thread-3: EventuelleDestination{id=6, acceuillantId=60} has been serialized to serialized_data/100_560_60.json... (输出将继续,但始终只有3个线程在活跃地执行任务)ExecutorService terminated. All tasks completed or cancelled.

从输出中可以看到,尽管我们提交了20个任务,但实际执行任务的线程(例如pool-1-thread-1、pool-1-thread-2、pool-1-thread-3)只有3个,这正是newFixedThreadPool(3)所实现的效果。

优雅关闭ExecutorService

正确关闭ExecutorService是并发编程中的一个重要环节。如果不在应用程序退出前关闭线程池,可能会导致程序无法正常终止,或者资源泄漏。shutdownAndAwaitTermination方法提供了一种优雅的关闭机制:

executorService.shutdown(): 启动有序关闭,不再接受新任务,但会允许已提交的任务(包括等待队列中的任务)完成执行。executorService.awaitTermination(timeout, unit): 阻塞当前线程,直到所有任务完成执行,或者超时发生,或者当前线程被中断。executorService.shutdownNow(): 如果awaitTermination超时,表示任务未能及时完成,此时可以调用shutdownNow()尝试立即停止所有正在执行的任务,并清空等待队列。此方法会向所有正在执行的线程发送中断信号。

注意事项与最佳实践

选择合适的线程池大小: newFixedThreadPool()的线程数量应根据CPU核心数、任务类型(CPU密集型或I/O密集型)和系统资源进行权衡。对于CPU密集型任务,通常建议线程数接近CPU核心数;对于I/O密集型

以上就是Java并发编程:使用ExecutorService限制线程数量的教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1058850.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
css伪元素::selection文本高亮样式实现
上一篇 2025年12月2日 05:14:12
用Partition Magic快速分区
下一篇 2025年12月2日 05:14:17

相关推荐

  • windows8如何取消开机密码_windows8取消开机密码的操作

    1、通过netplwiz取消密码验证并设置自动登录;2、在控制面板中更改密码为空实现无密码开机;3、使用命令提示符执行net user命令清除账户密码,三者均可实现Windows 8.1启动时跳过密码输入。 如果您希望在启动Windows 8系统时跳过手动输入密码的步骤,可以直接通过系统内置工具配置…

    2026年9月24日
    000
  • 赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会

    赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会赋能AI未来!康盈半导体 AI 应用存储新品登陆 elexcon 2025 展会

    8 月 26 日,中国电子、嵌入式及半导体先进封测行业的风向标 ——elexcon2025 深圳国际电子展暨嵌入式展盛大开幕。作为本届展会的重磅环节之一,国产存储领军品牌康盈半导体携新而来,以 “小而不凡,速启 ai 未来” 为核心主题,正式发布 2025 年存储新品,同步拉开面向 ai 终端应用的…

    2026年9月24日 • 用户投稿
    000
  • Java中固定长度用户ID输入验证:解决int类型长度检查问题

    本文详细介绍了在Java程序中如何实现用户输入固定长度ID的验证机制。针对常见的int cannot be dereferenced错误,我们将探讨将ID作为字符串读取并进行长度及格式校验的最佳实践,并提供处理字母数字型和纯数字型ID的示例代码,确保数据输入的准确性和程序的健壮性。 引言:用户输入验…

    2026年9月24日
    500
  • 手机qq浏览器阅读模式怎么退出_手机QQ浏览器退出阅读模式操作方法

    要退出手机QQ浏览器的阅读模式,首先点击页面顶部的“阅读模式已开启”按钮即可立即退出;若无明显开关,可刷新页面或长按刷新强制重新加载;也可通过右上角“更多”菜单选择“退出阅读模式”;为避免再次进入,可在设置中关闭“自动进入阅读模式”功能。 如果您在使用手机QQ浏览器时进入了阅读模式,但希望恢复到原始…

    2026年9月24日
    200
  • 数据实时迁移同步工具 CloudCanal v5.2.0.0 发布,支持 SaaS 全托管

    cloudcanal 免费社区版 是 clougence 公司推出的一款全自研、可视化、自动化数据迁移同步工具,具备 结构迁移、数据迁移、数据同步、数据校验、数据订正 等功能,支持 60+ 款流行关系型数据库、实时数仓、消息中间件、缓存数据库和搜索引擎之间数据互通,其中包含国产数据库 oceanba…

    2026年9月24日
    000
  • VSCode如何实现AI代码反混淆 VSCode智能分析混淆代码的技巧

    vscode没有一键ai反混淆功能,但可通过智能扩展、调试器、ast查看器、代码格式化工具及外部ai工具集成来辅助分析和逐步还原混淆代码;2. 利用eslint、prettier等扩展提升代码可读性,通过“重命名符号”“转到定义”“查找引用”等功能追踪变量和函数流向,结合多光标编辑和代码片段进行手动…

    2026年9月24日
    100
  • Laravel 表单验证失败后保留输入值:最佳实践教程

    本文旨在帮助 Laravel 开发者解决表单验证失败后,如何保留用户已输入数据的问题。我们将深入探讨 withInput() 方法的使用,并提供清晰的代码示例,确保即使在验证失败的情况下,用户体验也能保持流畅。通过本文的学习,你将掌握在 Laravel 中优雅地处理表单验证,并提升应用的可用性。 在…

    2026年9月24日
    000
  • 小红书推广选择阅读量还是粉丝量?小红书怎么推广引流

    小红书作为融合内容、社交与电商的综合性平台,近年来吸引了大量创作者和品牌入驻。在进行推广时,很多人常常纠结:是更重视阅读量,还是更关注粉丝量?本文将从两者的定义出发,分析各自的优劣势,并提供实用建议,帮助你制定适合自己的推广策略。 一、阅读量与粉丝量的本质区别 1. 阅读量 阅读量代表的是某篇笔记或…

    2026年9月24日
    000
  • 怎么在mysql中创建数据库表 mysql建表完整流程解析

    在 mysql 中创建数据库表的步骤包括:1) 选择合适的数据类型,如 int、varchar、timestamp;2) 设置索引,如主键和唯一索引;3) 应用约束条件,如 not null 和 unique;4) 设计表结构以满足业务需求,如使用 foreign key 和 enum;5) 优化性…

    2026年9月24日
    000
  • 如何通过日志排查权限问题

    排查权限问题需从日志入手,重点分析时间、用户、资源路径、拒绝原因及调用堆栈。首先检查应用日志中“用户无权访问”等提示,结合Web服务器日志中的403/401状态码定位请求异常;再查看操作系统日志如/var/log/secure中SSH或sudo拒绝记录,确认系统级权限问题;同时审查中间件如Sprin…

    2026年9月24日
    100
  • win10软件不兼容怎么办_win10软件兼容性处理方法

    首先使用兼容性疑难解答工具检测并修复问题,若无效则手动设置兼容模式为Windows 7或8,同时安装必要的Visual C++和.NET运行库,更新显卡等驱动程序,并尝试以管理员身份运行程序。 如果您尝试在Windows 10系统上运行某个软件,但出现“此应用无法在你的电脑上运行”或程序闪退等错误提…

    2026年9月24日
    000
  • 生成Java中全范围正Double随机数的正确方法

    本文旨在指导开发者如何在Java中生成覆盖整个正Double范围的随机数,并解释了使用ThreadLocalRandom.nextDouble(Double.MIN_VALUE, Double.MAX_VALUE)可能产生偏差的原因。我们将提供一种基于位操作的替代方案,确保生成的随机数在Double…

    2026年9月24日
    100
  • 《明末:渊虚之羽》1.6更新奖励领不了?官方手把手教学来了!

    《明末:渊虚之羽》是一款类魂动作角色扮演游戏,故事发生在巴蜀之地,此时正值黑暗动荡的明末,战事四起,一场神秘的疫病催生了妖怪一般的生物。 今天早些时候我们曾报道,游戏官方发布了1.6版本更新公告,补丁大小约为5.3GB,其中包括豪华版专属内容、免费头饰和性能优化等内容。 官方表示,本次更新“豪华扩展…

    2026年9月24日
    100
  • VSCode如何配置.NET开发环境 VSCode搭建.NET项目的完整流程

    首先安装.net sdk并验证版本;2. 安装vscode及microsoft官方c#扩展,确保智能感知和调试功能正常;3. 通过dotnet new命令创建项目,并使用code .在vscode中打开项目;4. 添加构建和调试资产以生成tasks.json和launch.json文件;5. 安装n…

    2026年9月24日
    000
  • PixVerse V5入围Artificial Analysis第一梯队,上线首日全球超百万用户更新并体验

    PixVerse V5入围Artificial Analysis第一梯队,上线首日全球超百万用户更新并体验PixVerse V5入围Artificial Analysis第一梯队,上线首日全球超百万用户更新并体验PixVerse V5入围Artificial Analysis第一梯队,上线首日全球超百万用户更新并体验PixVerse V5入围Artificial Analysis第一梯队,上线首日全球超百万用户更新并体验

    8月27日晚,根据权威独立测评平台 artificial analysis 最新测试结果,爱诗科技发布的pixverse v5 新一代自研视频生成大模型,在图生视频(image to video)项目中排名全球 top2,在文生视频(text to video)项目中位列 top3,保持在全球第一梯…

    2026年9月24日 • 用户投稿
    100
  • hive安装配置实验

    一、安装前的准备工作 1. 配置并安装hadoop,请参考链接http://blog.csdn.net/wzy0623/article/details/50681554。 2. 下载以下安装包:mysql-5.7.10-linux-glibc2.5-x86_64.tar.gz、apache-hive…

    2026年9月24日
    600
  • 动态表单输入中多答案数据处理教程

    本教程旨在解决Web开发中,如何高效处理包含动态数量答案的表单提交数据,特别是当需要更新现有问题及其关联答案时。文章将详细阐述前端表单的命名策略以及后端PHP如何解析这些动态输入,以准确获取答案内容及其对应的数据库ID,从而实现数据的精准更新,并提供最佳实践建议。 理解动态答案更新的挑战 在构建问答…

    2026年9月24日
    000
  • 大学论文怎么写?让AI工具助你一臂之力

    大学论文怎么写?让AI工具助你一臂之力大学论文怎么写?让AI工具助你一臂之力大学论文怎么写?让AI工具助你一臂之力大学论文怎么写?让AI工具助你一臂之力

    如果要选出大学学习过程中最令人头疼的事,写论文无疑能稳居榜首。从选题开题、内容撰写,到翻译润色、查重降重,每个步骤都耗时耗力,让人焦头烂额。然而,随着 ai 技术的发展,如今写论文这件事,已经可以借助智能工具变得更高效、更轻松。 开题太难?AI 来帮你破局! 论文的第一道难关就是开题。面对浩如烟海的…

    2026年9月24日 • 用户投稿
    100
  • Java Stream API:从嵌套集合中提取唯一值的两种高效方法

    本文详细介绍了如何利用Java Stream API中的flatMap()和mapMulti()操作,高效地从包含嵌套列表的复杂数据结构(如List中包含List)中提取并收集唯一的元素(如城市名称),替代传统的嵌套循环,提升代码的简洁性和可读性。 在java编程中,我们经常会遇到处理复杂数据结构的…

    2026年9月24日
    100
  • iSlide预览功能如何开启_iSlide预览功能开启的完整指南

    首先确认iSlide插件已正确安装并显示在PowerPoint功能区,若未显示需重新安装;接着进入“iSlide”选项卡,使用“资源库”中主题或图表分类,将鼠标悬停于缩略图以触发预览;如无反应,检查是否已登录账户且网络畅通,避免防火墙限制;随后更新iSlide至最新版本,卸载旧版后从官网下载安装,并…

    2026年9月24日
    200

发表回复

登录后才能评论
关注微信