【JavaEE】线程池和定时器
🔥个人主页: 中草药
🔥专栏:【Java】登神长阶 史诗般的Java成神之路
✏️一.线程池
在Java中,线程池(Thread Pool)是一种用于管理并发线程的机制,它提供了一种创建、复用和管理一组线程的方法,这些线程可以用来执行提交的任务。线程池的使用可以显著提高程序的性能和响应能力,尤其是在处理大量并发任务时。Java的java.util.concurrent
包提供了丰富的API来创建和管理线程池,其中最核心的接口是ExecutorService
,以及其实现类ThreadPoolExecutor
。
创建
Java中线程池的创建主要通过Executors
工厂类或直接使用ThreadPoolExecutor
类来完成:
-
使用
Executors
工厂类:newSingleThreadExecutor()
:创建一个单线程的线程池,它只会创建一个线程来执行任务。newFixedThreadPool(int nThreads)
:创建一个固定大小的线程池,线程数量由nThreads
参数确定。newCachedThreadPool()
:创建一个可缓存的线程池,线程数量没有上限,当长时间没有新任务时,空闲线程会被终止。newScheduledThreadPool(int corePoolSize)
:创建一个可以安排任务的线程池,可以指定延迟执行任务或定期执行任务。
-
使用
ThreadPoolExecutor
类:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
:这是一个构造函数,可以自定义线程池的核心参数,如核心线程数、最大线程数、线程空闲时间、工作队列等。
使用
- 提交任务:使用
execute(Runnable command)
或submit(Callable<T> task)
方法提交任务到线程池。 - 关闭线程池:使用
shutdown()
方法关闭线程池,等待所有已提交的任务完成;使用shutdownNow()
方法立即关闭线程池并尝试取消正在执行的任务。
举例
- 使用Executors.newFixedThreadPool (5) 能创建出固定包含10个线程的线程池
- 返回值类型为ExecutorService
- 通过ExecutorService.submit可以注册一个任务到线程池中
public static void main(String[] args) throws InterruptedException {ExecutorService service= Executors.newFixedThreadPool(5);for (int i = 0; i < 100; i++) {int id=i;service.submit(()->{System.out.println("执行任务"+id+";"+Thread.currentThread().getName());});}// 最好不要立即就终止, 可能使任务还没执行完呢, 线程就被终止了.Thread.sleep(2000);// 关闭线程池service.shutdown();System.out.println("程序退出");}
线程池配置参数分析*

-
核心线程数-CorePoolSize
- 参数名:
corePoolSize
- 描述:线程池中保持的线程的最少数量。即使线程池中没有任务需要执行,只要线程的数量不超过
corePoolSize
,线程池就不会销毁线程。这样可以保证线程池随时准备接收新的任务,而不需要频繁地创建和销毁线程。
- 参数名:
-
最大线程数-MaximumPoolSize
- 参数名:
maximumPoolSize
- 描述:线程池中允许的最大线程数量。当线程池中的活动线程数量达到
maximumPoolSize
后,线程池不会创建新的线程,而是将任务放入工作队列中等待执行。如果工作队列已满,线程池会采取拒绝策略处理新任务。
- 参数名:
-
空闲线程存活时间-KeepAliveTime
- 参数名:
keepAliveTime
- 描述:当线程池中的线程数量超过
corePoolSize
时,超出的线程会在没有任务可执行时等待的时间长度。如果在keepAliveTime
时间内没有新任务到来,超出的线程将被终止。这有助于控制线程池的规模,避免在任务量减少时线程资源的浪费。
- 参数名:
-
时间单位-Unit
- 参数名:
unit
- 描述:配合
keepAliveTime
使用,指定空闲线程存活时间的单位,如毫秒、秒、分钟等。
- 参数名:
-
工作队列-WorkQueue
- 参数名:
workQueue
- 描述:用于存放等待执行的任务的阻塞队列。当线程池中的线程数量达到
corePoolSize
且所有线程都在执行任务时,后续到达的任务会被放置在工作队列中等待执行。常见的队列类型有ArrayBlockingQueue
(有界队列)、LinkedBlockingQueue
(有界或无界队列)、SynchronousQueue
(不存储元素的队列)等。
- 参数名:
-
拒绝策略-RejectedExecutionHandler
- 参数名:
handler
- 描述:当线程池无法接受新任务时(即线程数量达到
maximumPoolSize
且工作队列已满)所采取的策略。
- 参数名:
常见的拒绝策略
AbortPolicy
:抛出RejectedExecutionException
异常。CallerRunsPolicy
:由调用者线程执行该任务,即直接在调用execute
方法的线程中执行任务。DiscardPolicy
:静默丢弃无法处理的任务。DiscardOldestPolicy
:丢弃队列中最老的任务,并尝试再次提交新任务。
模拟实现
- 核心操作为submit,将任务加入线程池
- 使用Runnable描述一个任务
- 使用一个BlockingQueue组织所有任务
- 不停从BlockingQueue中取任务并执行
class MyThreadPool{private BlockingQueue<Runnable> queue=new ArrayBlockingQueue<>( 1000);public MyThreadPool(int n){//此处的n表示可以创建几个线程for (int i = 0; i < n; i++) {Thread t=new Thread(()->{while(true){try {Runnable runnable = queue.take();runnable.run();} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();}}/*** 添加任务* @param runnable*/public void submit(Runnable runnable){try{queue.put(runnable);}catch (InterruptedException e){throw new RuntimeException(e);}}
}
测试代码
public static void main(String[] args) {MyThreadPool pool=new MyThreadPool(5);for (int i = 0; i < 100; i++) {int id=i;pool.submit(()->{System.out.println("执行任务"+id+";"+Thread.currentThread().getName());});}}
测试结果
✒️二.定时器
在Java中,定时器(Timer)是一个重要的工具,用于执行定时任务,无论是单次的还是周期性的,类似于一个闹钟,到达一个设定的时间之后就会执行某个指定好的代码Java提供了多种方式来实现定时任务。
基本使用
其中最常用的是java.util.Timer
类和java.util.concurrent.ScheduledExecutorService
接口。下面将详细介绍这两种实现方式。
java.util.Timer
类
java.util.Timer
是一个简单的定时器类,它使用一个单独的守护线程来调度和执行任务。它通常与java.util.TimerTask
一起使用,后者是java.util.Timer
类的任务执行单元。
public static void main(String[] args) {Timer timer=new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello 3");}},3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello 2");}},2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello 1");}},1000);}
注意事项
- 在使用定时器时,一定要处理好任务执行中可能出现的异常,否则守护线程可能因为异常而终止,导致定时器失效。
- 确保在应用程序结束前正确地关闭定时器,避免线程泄露和资源浪费。
- 考虑到线程安全和并发控制,
ScheduledExecutorService
在大多数情况下是更好的选择,尤其是当任务需要并发执行时。
模拟实现
package demo;import java.util.PriorityQueue;
import java.util.Timer;
import java.util.TimerTask;/*** 模拟实现定时器*/
class MyTimerTask implements Comparable<MyTimerTask> {private Runnable runnable;private long time;//此处的time是毫秒时间戳,表示这个任务具体啥时候执行public MyTimerTask (Runnable runnable,long delay){this.runnable=runnable;this.time=System.currentTimeMillis()+delay;}public void run(){runnable.run();}public long getTime(){return time;}@Overridepublic int compareTo(MyTimerTask o) {// 此处这里的 - 的顺序, 就决定了这里是大堆还是小堆.// 此处需要小堆.return (int)(this.time-o.time);}
}class MyTimer{private PriorityQueue<MyTimerTask> queue=new PriorityQueue<>();//如果用List不是最好的选择,在后续执行列表中任务时,需要依次遍历每个元素private Object locker=new Object();//创建线程,负责执行上述队列中的内容public MyTimer(){Thread t=new Thread(()->{try{while(true){synchronized(locker){while(queue.isEmpty()){locker.wait();}MyTimerTask current=queue.peek();if (System.currentTimeMillis()>=current.getTime()){//执行该任务current.run();//将执行过的任务从队列中删除queue.poll();}else{//暂且不执行locker.wait(current.getTime()-System.currentTimeMillis());}}}}catch (InterruptedException e){throw new RuntimeException(e);}});t.start();}public void schedule(Runnable runnable,long delay){synchronized(locker){MyTimerTask current=new MyTimerTask(runnable,delay);queue.offer(current);locker.notify();}}}
测试代码
public static void main(String[] args) {MyTimer timer=new MyTimer();timer.schedule(()->{System.out.println("倒计时 2");},2000);timer.schedule(()->{System.out.println("倒计时 1");},3000);timer.schedule(()->{System.out.println("倒计时 3");},1000);//证明优先级队列的作用}
结果
🖋️三.总结与反思
在深入研究Java并发编程的过程中,线程池和定时器是两个不可或缺的概念。它们不仅体现了Java在处理并发和定时任务上的强大功能,也反映了现代软件工程中资源管理和效率优化的重要性。以下是我在学习这两个主题后的总结与反思。
线程池:高效资源管理的艺术
线程池,顾名思义,是一个预先创建好的线程集合,用于执行提交的任务。它通过复用现有线程,避免了线程创建和销毁的开销,从而提高了系统的响应速度和资源利用率。线程池的配置参数,如核心线程数、最大线程数、工作队列类型等,需要根据具体的应用场景和系统资源进行细致调整。合理配置的线程池能够显著提升并发任务的处理能力,减少资源浪费,是高性能服务器应用的基石。
在实际应用中,我深刻体会到线程池的配置需要平衡并发度与系统负载。过多的线程可能导致系统资源紧张,而过少的线程则可能无法充分利用系统资源。此外,线程池的管理,如任务的排队策略、异常处理、线程的生命周期管理等,也是实现高效、健壮系统的关键。
定时器:精准时间控制的利器
定时器则是在Java中执行定时任务的重要工具。无论是简单的java.util.Timer
,还是更强大的java.util.concurrent.ScheduledExecutorService
,它们都提供了执行周期性或一次性定时任务的能力。定时器的使用极大地丰富了Java在事件驱动、任务调度等方面的功能,是实现自动化运维、数据定时处理等场景的基础。
在使用定时器时,我注意到几个关键点:首先,选择合适的定时器类型至关重要。Timer
简单易用,但ScheduledExecutorService
提供了更高级的调度策略和更好的并发支持。其次,定时任务的异常处理不容忽视,否则可能导致任务执行失败而不被察觉。最后,正确关闭定时器,避免线程泄露,是编写健壮程序的必要步骤。
反思
通过学习和实践线程池与定时器,我深刻理解到并发编程的复杂性和魅力。在设计系统时,不仅要考虑功能的实现,还要兼顾性能、资源管理和错误处理。线程池和定时器的合理应用,能够显著提升软件的响应速度和稳定性,但同时也对程序员提出了更高的要求。
面向未来,随着云计算和分布式系统的普及,线程池和定时器的概念将更加重要。如何在分布式环境中高效、安全地管理线程和执行定时任务,将成为新一代软件工程师面临的新挑战。掌握线程池和定时器的原理及最佳实践,无疑将为迎接这些挑战打下坚实的基础。
总之,Java中的线程池和定时器不仅是编程技巧的体现,更是现代软件工程思想的反映。通过深入学习和不断实践,我期待能够将这些知识应用于更复杂的系统设计和开发中,创造出既高效又可靠的软件产品。
🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀
以上,就是本期的全部内容啦,若有错误疏忽希望各位大佬及时指出💐
制作不易,希望能对各位提供微小的帮助,可否留下你免费的赞呢🌸
相关文章:

【JavaEE】线程池和定时器
🔥个人主页: 中草药 🔥专栏:【Java】登神长阶 史诗般的Java成神之路 ✏️一.线程池 在Java中,线程池(Thread Pool)是一种用于管理并发线程的机制,它提供了一种创建、复用和管理一组…...

《Unity3D网络游戏实战》通用服务器框架
服务端程序的两大核心是处理客户端的消息和存储玩家数据 模块划分 游戏流程 连接阶段:客户端调用Connect连接服务端即为连接阶段。连接后双端即可通信,但服务端还不知道玩家控制的是哪个角色。于是客户端需要发送一条登录协议,协议中包含用户…...
LeetCode404 左叶子之和
前言 题目: 404. 左叶子之和 文档: 代码随想录——左叶子之和 编程语言: C 解题状态: 成功解答! 思路 注意左叶子节点的定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是…...
nodejs操作redis的工具类
const Redis require("ioredis");async function generateStreamID() {// 生成时间戳(毫秒级)const timestamp Date.now();// 生成唯一的序列号const sequenceNumber Math.random() * 1000; // 根据需要生成唯一的序列号// 构建 Stream ID&…...
关于wsl2与win11互联互通的问题
首先搞清楚使用场景。我是在win11上写go做后端api,在WSL2 的Linux上写前端页面。 我发现在windows 里写go语言没啥问题,我的后端api部署在win11上。但是在win11上写前端经常会遇到莫名其妙的故障,一会npm包下不来一会说包之间的依赖结构出问题…...

C++ 类型转换
目录 0.前言 1.C语言类型转换 1.1隐式类型转换 1.2显式类型转换 2.C强制类型转换 2.1 static_cast 2.2 reinterpret_cast 2.3 const_cast 2.4 dynamic_cast 3.为什么C需要4种强制类型转换 3.1类型转换的多样性需求 3.2提高类型转换的安全性 3.3提供更明确的语义 3.4支持高级编程…...

2024挖漏洞给报酬的网站汇总,兼职副业3天收益2k
文章目录 一、众测平台(国内)二、前沿漏洞研究奖励计划三、行业SRC四、企业应急响应中心-SRC-汇总 1、互联网企业2、生活服务、住宿、购物相关企业3、物流、出行、旅游4、金融相关企业5、视频游戏直播社交娱乐6、教育、问答、知识付费7、泛科技通讯物联网云服务8、安全企业9、其…...

0到1学习Google广告(2):掌握展示位置及排名规则丨出海笔记
大家好, 我是专注谷歌广告和谷歌SEO的谷哥哥哥,感谢出海笔记Alan邀请。今天我们来聊聊广告界的大拿——谷歌广告。在这个数字营销的黄金时代,无论是B2B、B2C还是品牌类客户,谷歌广告都是一个不容忽视的战场。那么,如何在这个战场上…...

MySQL数据库读超时/SELECT查询超时 杂记
本文环境 阿里云RDS MySQL 8.0.34 当客户端向MySQL数据库发送一条SQL之后,由于SQL很慢很慢,它会在什么时候结束呢? 查看 max_execution_time 变量值 mysql> show variables like max_execution_time; --------------------------- | Variable_name | Value | ------…...

docker数据卷:
docker数据卷: 容器和宿主机之间数据共享 容器和宿主机之间数据共享——————挂载卷————容器内的目录和宿主机的目录进行挂载,实现数据文件共享 容器的生命周期有限,一旦重启所有对容器内部文件数据的修改以及保存的数据都会被初始…...

【linux】linux中如何通过systemctl来创建和管理服务
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…...

WPF-实现多语言的静态(需重启)与动态切换(不用重启)
目录 一、多语言切换(需重启) 1、配置文件添加Key 2、新增附加属性当前选择语言 3、创建资源文件 4、初始化多语言集合 5、切换多语言并更新配置文件 6、应用程序启动根据配置切换多语言 7、使用 二、多语言切换(无需重启)…...

UE5学习笔记12-为角色添加蹲下的动作
一、一点说明 1.蹲下使用了ACharacter类中Crouch();函数,函数功能是先检查是否存在运动组件,将bool类型的变量变为true,该变量代表是想要蹲下。 2.通过源码可知存在是否蹲下的bool变量bIsCrouched如图,如果对:1有疑问请搜索C位域 …...
【笔记】Android 多用户模式和用户类型
简介 用户界面:System 》Multiple Users 》 开关多用户模式。 一般是不同用户模式下,有修改Settings应用配置的权限差异,因此需要通过用户类型对功能进行判断限制。 代码 通过UserManager可以获取当前用户的信息。 frameworks/base/core/…...

SQL基础——MySQL的索引
简介:个人学习分享,如有错误,欢迎批评指正。 一、概述 介绍 索引是通过某种算法,构建出一个数据模型,用于快速找出在某个列中有一特定值的行,不使用索引,MySQL必须从第一条记录开始读完整个表&…...
【开发语言】面向对象和面向过程开发思路的区别
引入: 我总结了 面向过程的开发语言思路:1.我要干啥?2.怎么才能实现 面向对象的开发语言思路:1.我要研究谁?2.他能干啥 详解: 面向过程的开发语言思路 我要干啥? 在面向过程的开发中&a…...

谷歌账号登录的时候提示被停用,原因是什么,账号还有救吗?该如何处理?
今日早上,有个久违的朋友找到我说,要恢复账号。 他的情况是这样的:7月21日的时候,他发现自己的谷歌账号登录的时候提示活动异常先,需要输入手机号码验证才能恢复账号。但是输入了自己和亲友们的多个手机号码都无法验证…...
数据库复习笔记
写在最前, 写文章的初衷只是为了复习与记录自己的成长,笔者目前水平还有待提高,文章中难免会出现许多问题与错误,文章内容仅供参考,有不足的地方还请大家多多包涵并指正,谢谢~ 第八章 T-SQL程序结构 8.…...

学习STM32(6)-- STM32单片机ADCDAC的应用
1 引 言 深入了解并掌握STM32F103单片机在模拟数字转换(ADC)和数字模拟转换(DAC)应用方面的功能和操作。学习如何配置STM32F103的ADC模块,实现模拟信号到数字信号的精确转换;同时,探索DAC模块…...
学习记录第二十五天
wait函数 wait函数是一个系统调用,用于等待一个子进程结束并回收其资源。当父进程调用wait函数时,它会暂停执行,直到至少有一个子进程结束。wait函数的原型如下: #include <sys/types.h> #include <sys/wait.h>pid_…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...

C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...

DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...

逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...