ExecutorService介绍

参考:https://blog.csdn.net/fwt336/article/details/81530581

前言

        在开发中为了提高系统的响应速度和处理能力会使用到多线程,但线程的创建和释放,需要占用不小的内存和资源。如果每次需要使用线程时,都new 一个Thread的话,难免会造成资源的浪费,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。不利于扩展。就像MySQL数据库连接一样,每创建一个连接都需要消耗资源,所以就引入了数据库连接池,线程也引入了线程池的概念,需要线程时可以不用创建,直接从池中获取,在JDK中就为我们提供了ExecutorService。

ExecutorServiceExecutorService 提供了几种不同类型的线程池,包括单线程池、固定大小线程池、可缓存线程池和定时任务线程池。通过这些线程池,我们可以有效地管理多个任务的执行,并且可以控制线程池的大小,可以有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,同时提供定时执行、定期执行、单线程、并发数控制等功能,也不用使用TimerTask了。

1. ExecutorService的创建方式

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)

所有线程池最终都是通过这个方法来创建的。

corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;达到核心线程数但没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。

maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。

keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。

unit : 时间单位,TimeUnit.SECONDS等。

workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。

threadFactory : 线程工程,用于创建线程。

handler : 当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序

2.线程池的类型

2.1 可缓存线程池

ExecutorService cachePool = Executors.newCachedThreadPool();

看看它的具体创建方式:

public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, //核心线程数Integer.MAX_VALUE,  //线程池大小60L,  //空闲保存时间TimeUnit.SECONDS, new SynchronousQueue<Runnable>() //阻塞队列);
}

通过它的创建方式可以知道,创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式。它只适用于生命周期短的任务。

2.2 单线程池

ExecutorService singlePool = Executors.newSingleThreadExecutor();

顾名思义,也就是创建一个核心线程:

public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));}

只用一个线程来执行任务,保证任务按FIFO顺序一个个执行。

2.3 固定线程数线程池

Executors.newFixedThreadPool(3);
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

也就是创建固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行。

2.4 固定线程数,支持定时和周期性任务

ExecutorService scheduledPool = Executors.newScheduledThreadPool(5);
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE,DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,new DelayedWorkQueue());
}

可用于替代handler.postDelay和Timer定时器等延时和周期性任务。

问题:最大线程数量为Integer最大值,不就是可以创建很多个空闲线程用来处理任务吗?为什么会任务还会被放入工作队列中等待处理

      在 ScheduledThreadPoolExecutor 的构造方法中,将最大线程数量设置为 Integer.MAX_VALUE。这意味着线程池可以创建非常多的空闲线程来处理任务。然而,即使存在大量空闲线程,仍然会将任务放入工作队列中等待处理的原因是为了减少线程创建和销毁的开销,并且能够更好地控制线程的数量。通过工作队列,线程池可以根据任务的到达速率和线程的处理能力来动态调整任务的执行顺序和并发度,以保证任务能够按时得到执行。因此,虽然 ScheduledThreadPoolExecutor 可以创建大量的空闲线程,但为了更好地管理和控制线程数量,任务仍然会被放入工作队列中等待处理,以实现更高效的任务调度和执行。

public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

scheduleAtFixedRatesheduleWithFixedDelay有什么不同呢?

scheduleAtFixedRate: 用于以固定的时间间隔执行任务。它接受一个 Runnable 类型的参数以及两个 long 类型的参数:initialDelay(初始延迟时间)和 period(任务执行的时间间隔)。该方法会在 initialDelay 时间后开始执行第一次任务,然后每隔 period 时间执行一次。如果任务的执行时间超过了指定的时间间隔 period,那么下一次任务的执行会立即开始,不会等待上一次任务的完成。这意味着任务的执行可能会重叠。

sheduleWithFixedDelay: 在任务执行完成后,等待 delay 时间后再开始下一次任务的执行。这样可以确保任务之间有固定的时间间隔,并且不会重叠执行。

2.5 手动创建线程池

private ExecutorService pool = new ThreadPoolExecutor(3, 10,10L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(512), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

可以根据自己的需求创建指定的核心线程数和总线程数。

3. 应用场景

  1. 并发任务执行:ExecutorService 可以用于执行并发的异步任务,通过线程池的方式,可以有效地管理和复用线程资源,提高任务执行的效率。
  2. 定时任务调度:ExecutorService 可以用于定时执行任务,通过调用 schedule() 或 scheduleAtFixedRate() 方法,可以实现按照指定的时间间隔或固定频率执行任务的功能。
  3. 大规模数据处理:当需要对大规模数据进行处理时,可以将数据分割成多个任务,并提交给 ExecutorService 执行,以并发的方式对数据进行处理,提高处理速度。

4. 代码案例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ConcurrentTaskExample {public static void main(String[] args) {// 创建固定大小的线程池ExecutorService executorService = Executors.newFixedThreadPool(5);// 提交 10 个任务给线程池执行for (int i = 0; i < 10; i++) {int taskId = i;executorService.submit(() -> {System.out.println("任务 " + taskId + " 开始执行");// 模拟任务执行耗时try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("任务 " + taskId + " 执行完成");});}// 关闭线程池executorService.shutdown();try {// 等待所有任务完成或超时(这里设置超时时间足够大)if (!executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS)) {// 如果超时仍有任务未完成,则强制关闭线程池executorService.shutdownNow();System.out.println("等待超时,强制关闭线程池");}} catch (InterruptedException e) {// 捕获中断异常e.printStackTrace();executorService.shutdownNow();}}
}

结果任务不是按进来无序完成的,任务1耗时并不会阻塞程序对任务2的处理。

5. 注意点

在上面的代码中需要关闭线程池,线程池的关闭可以确保线程池中的线程在不再需要时被正确地释放和销毁。不关闭线程池可能在某些情况下工作正常,但它可能导致一些潜在问题:

  1. 资源泄漏: 如果不关闭线程池,它将一直保持活动状态,并且线程池中的线程将继续存在。这会导致资源的浪费,特别是在长时间运行的应用程序中。

  2. 系统负载: 线程池中的线程将占用系统资源,包括内存和处理器。如果不关闭线程池,这些资源将被持续占用,可能导致系统负载增加。

  3. 程序退出延迟: 如果主程序不关闭线程池并等待线程池中的任务完成,那么程序可能会在所有任务完成之前退出。这可能导致任务未执行完全或结果未被处理。所以代码中设置了一个足够大的超时时间,确保任务完全执行完。

因此,为了避免以上问题,建议在不再需要线程池时,显式地调用 shutdown() 方法来关闭线程池。这将停止线程池接受新的任务,并开始逐渐关闭线程池中的线程,直到所有任务都执行完毕。

之前部署过一个SpringBoot项目在云服务器上,总是运行一阵子服务器就会宕机,内存占满了,但是这个项目只有我一个人在用,不存在Redis占用过高内存的问题,后来排查到是我使用线程池后没有关闭,导致了系统资源耗尽。即使线程池的线程数是固定的,也会造成资源的消耗。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/207508.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】2723. 两个 Promise 对象相加

两个 Promise 对象相加 题目题解 题目 给定两个 promise 对象 promise1 和 promise2&#xff0c;返回一个新的 promise。promise1 和 promise2 都会被解析为一个数字。返回的 Promise 应该解析为这两个数字的和。 示例 1&#xff1a; 输入&#xff1a; promise1 new Promise…

geoserver根据属性字段值设置不同的颜色

<?xml version"1.0" encoding"UTF-8"?> <StyledLayerDescriptor xmlns"http://www.opengis.net/sld" xmlns:xlink"http://www.w3.org/1999/xlink" xmlns:ogc"http://www.opengis.net/ogc" xmlns:xsi"http:/…

中国聚羟基脂肪酸酯(PHA)市场评估与投资战略报告(2024版)

内容简介&#xff1a; PHA英文名称为 Polyhydroxyalkanoates&#xff0c;是近20多年迅速发展起来的&#xff0c;很多天然原料合成的一种聚酯。PHA是生物基生物降解材料&#xff0c;具有良好的生物相容性和可加工性。防止水汽的穿透是保鲜包装中的重要指标&#xff0c;PHA 有良…

os.walk()遍历文件夹/文件

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

P3 Qt 控件 —— pushButton

前言 &#x1f3ac; 个人主页&#xff1a;ChenPi &#x1f43b;推荐专栏1: 《C_ChenPi的博客-CSDN博客》✨✨✨ &#x1f525; 推荐专栏2: 《Linux C应用编程&#xff08;概念类&#xff09;_ChenPi的博客-CSDN博客》✨✨✨ &#x1f33a;本篇简介 &#xff1a;这一章我们学一…

Python evalml 库:自动化机器学习的新前景

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 在机器学习领域&#xff0c;evalml 库崭露头角&#xff0c;为开发者提供了一个强大而高效的自动化机器学习框架。本文将深入介绍 evalml 的核心功能、使用方法以及在实际项目中的应用。通过详实的示例代码&#…

前端高频面试题大全-面试必看

内容较多&#xff0c;建议查看目录&#xff0c;方便食用 高频 React和Vue的区别 通常解法&#xff1a;vue是采用指令结合vue-loader实现构件用户界面的渐进式框架&#xff0c;React是采用JSX构件用户界面的组件化开发 详细解法&#xff1a;在渲染界面的时候DOM操作是昂贵的&…

【Linux系统编程】项目自动化构建工具make/Makefile

介绍&#xff1a; make和Makefile是用于编译和构建C/C程序的工具和文件。Makefile是一个文本文件&#xff0c;其中包含了编译和构建程序所需的规则和指令。它告诉make工具如何根据源代码文件生成可执行文件&#xff0c;里面保存的是依赖关系和依赖方法。make是一个命令行工具&a…

智汇恒星科技|控乐屋.全宅智能冠军代言来啦, 智慧家居千亿蓝海

随着5G、大数据、云计算、物联网等技术的发展&#xff0c;智能化正覆盖人们生活的方方面面&#xff0c;全屋智能的出现为“一键式”智能家居生活享受提供无限可能。近年来智能家居行业总体规模增长迅速&#xff0c;数据显示&#xff0c;2022年中国智能家居行业市场规模约为6200…

Java内部类

文章目录 什么是 Java 中的内部类&#xff1f;有哪些类型的内部类&#xff1f;匿名内部类局部内部类&#xff08;定义在方法中的类&#xff09;局部内部类静态内部类 Java 类中不仅可以定义变量和方法&#xff0c;还可以定义类&#xff0c;这样定义在类内部的类就被称为内部类。…

Java期末复习题之封装

点击返回标题->23年Java期末复习-CSDN博客 第1题. 定义一个类Person,定义name和age私有属性&#xff0c;定义有参的构造方法对name和age进行初始化。在测试类中创建该类的2个对象&#xff0c;姓名、年龄分别为lili、19和lucy、20&#xff0c;在屏幕打印出2个对象的姓名和年龄…

2024年江苏省职业院校技能大赛信息安全管理与评估 理论题(样卷)

2024年江苏省职业院校技能大赛信息安全管理与评估 理论题&#xff08;样卷&#xff09; 理论技能与职业素养&#xff08;100分&#xff09; 2024年江苏省职业院校技能大赛&#xff08;高职学生组&#xff09; 模块三“信息安全管理与评估”理论技能 【注意事项】 Geek极安云…

Qt对excel操作

Qt库中自带对excel操作的模块QAxObject&#xff0c;QAxObject是Qt提供给程序员从代码中访问Office的对象类&#xff0c;其本质上是一个面向微软操作系统的COM接口。 QAxObject将所有Office的工作簿、表格、文档等都作为其子对象&#xff0c;程序员通过调用querySubObject()这个…

vue-quill-editor上传图片base64转化为img标签

vue-quill-editor传图片的话默认把图片转成了base64&#xff0c;会导致我们的参数特别大&#xff0c;不好渲染 基于vue-quill-editor重写一个quill-editor组件 <template><div><quilleditorv-model"content"ref"myTextEditor":options&qu…

在线图片转Base64图片的方法

html版(不包含跨域解决,输入在线图片地址即可转换) <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>…

Cisco 思科路由交换网络设备 安全基线 安全加固操作

目录 账号管理、认证授权 本机认证和授权ELK-Cisco-01-01-01 设置特权口令 ELK-Cisco-01-02-01 ELK-Cisco-01-02-02 ​​​​​​​登录要求 ELK-Cisco-01-03-01 ​​​​​​​ELK-Cisco-01-03-02 ELK-Cisco-01-03-03 日志配置 ELK-Cisco-02-01-01 通信协议 ELK-Cisco-…

软考高级信息系统项目管理师经验分享

考了三次终于考过了。 第一次&#xff0c;没准备&#xff0c;打酱油。 第二次&#xff0c;不舍得花钱&#xff0c;自己复习&#xff0c;结果教材改版了都不知道。 第三次&#xff0c;改机考了。 总结一下这三次的经验&#xff1a; 一、不是学霸型的一定要报班 报班的优点&…

实时动作识别学习笔记

目录 yowo v2 yowof 判断是在干什么,不能获取细节信息 yowo v2 https://github.com/yjh0410/YOWOv2/blob/master/README_CN.md ModelClipmAPFPSweightYOWOv2-Nano1612.640ckptYOWOv2-Tiny

【prometheus】入门+实习需求实战

prometheus官方教程 说明&#xff1a; 感冒了&#xff0c;写点总结不浪费时间&#xff0c;声明不会扯出来任何的私有代码&#xff0c;毕竟要保密&#xff0c;只会讲prometheus的官方有的&#xff0c;以及那些开源的代码方法&#xff0c;例如prometheus客户端代方法&#xff0…

QML中Dialog获取close与open状态

1.新建MyDialog.qml import QtQuick 2.15import QtQuick.Dialogs 1.2Dialog {id: rootvisible: falsetitle: qsTr("弹出对话框")width: 250height: 200} 2.main.qml中调用MyDialog import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.15…