当前位置: 首页 > news >正文

Java多线程详解①①(全程干货!!!) 实现简单的线程池 || 定时器 || 简单实现定时器 || 时间轮实现定时器

这里是Themberfue

· 上一节讲了 线程池 线程池中的拒绝策略 等相关内容 


实现简单的线程池 

· 一个线程池最核心的方法就是 submit,通过 submit 提交 Runnable 任务来通知线程池来执行 Runnable 任务

· 我们简单实现一个特定线程数量的线程池,这些线程应该在线程池创建之初就被创建好,并不断尝试从任务队列中取出任务从而执行

· 所以还需一个阻塞队列用于存储 submit 提交过来的任务

· 代码实现:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;/*** @author: Themberfue* @date: 2024/10/21 21:20* @description:*/
// 实现一个固定线程数的线程池
class MyThreadPool {private BlockingQueue<Runnable> queue = null;public MyThreadPool(int n) {// 初始化线程池,创建固定个数的线程// 这里使用ArrayBlockingQueue作为任务队列, 容量为1000this.queue = new ArrayBlockingQueue<>(1000);// 创建 n 个线程for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {try {while (true) {Runnable take = queue.take();take.run();}} catch (InterruptedException e) {throw new RuntimeException(e);}});// 默认为前台线程,不会正常退出程序// t.setDaemon(true);t.start();}}public void submit(Runnable task) throws InterruptedException {// 将任务放入队列中queue.put(task);}
}public class Demo34 {public static void main(String[] args) throws InterruptedException {MyThreadPool threadPool = new MyThreadPool(10);// 向线程池提交任务for (int i = 0; i < 100; i++) {int id = i;threadPool.submit(() -> {System.out.println("执行任务:" + id + " " + Thread.currentThread().getName());});}}
}

· 在执行完 100 个任务后,会发现程序并未结束,那是因为程序阻塞在了 queue.take() 中;况且线程池中的线程默认是前台线程,故不会随着主线程的结束而结束

· 在上一节的代码,看到了 shutdown 操作,也就是关闭线程池操作,该操作可以让线程池里的全部线程全部关闭,但不能保证线程池里的任务一定全部执行完毕;

· 除此之外,还提供了 awaitTermination 操作,该操作在线程池中的线程里的任务全部执行完毕后,再关闭线程池~

· 选择使用 shutdown 还是 awaitTermination 取决于任务的重要程度~


定时器 

· 定时器类似于闹钟,时间到了就执行相关的逻辑~

· Java 标准库中提供了 Timer 类中的 schedule 方法来延迟执行某些逻辑~:

import java.util.Timer;
import java.util.TimerTask;/*** @author: Themberfue* @date: 2024/10/22 19:24* @description:*/
public class Demo35 {public static void main(String[] args) {Timer timer = new Timer();// TimerTask是抽象类,但这里是通过创建 "匿名内部类" 的方式创建类timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("3秒过后执行");}}, 3000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2秒过后执行");}}, 2000);timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1秒过后执行");}}, 1000);System.out.println("立即执行");}
}

· 与 Runnable 类似,这里描述任务的是 TimerTask 抽象类,通过创建一个匿名类继承了 TimerTask 类并且重写了 run 方法~


简单实现定时器 

· 首先得有一个任务类TimerTask描述该任务逻辑

· 此外还需一个集合类来管理不同时间段要执行的任务;使用哪个集合类呢?必须得按照时间顺序执行任务,且遍历时间复杂度不能太高~ 没错,使用 优先级队列,可以快速找到哪个任务先执行

· 还需一个 schedule 方法来将任务添加到队列中

· 最后还需要额外创建一个线程来执行任务里的逻辑;此线程还需额外添加判断逻辑,与线程池的线程不同,该线程需要时间到了之后才可以执行相应的任务逻辑,时间没到就不执行,而不是直接执行

· 如何判断某任务到了时间需要执行?我们借助 时间戳 来完成这一功能,在创建 TimerTask 类时将当前 时间戳 与需要延迟执行的时间加上,循环判断当前时间戳是否达到加上后的结果;如果时间戳来到了这一结果,就说明可以执行该任务了

· 切记:既然要创建小根堆,我们需要规定该堆是按什么数值排序;在该案例中,应该是以执行该任务的延迟时间为基准,所以我们需要额外自定义一个比较器,用于小根堆的排序规则

import java.util.PriorityQueue;/*** @author: Themberfue* @date: 2024/10/22 19:32* @description:*/
class MyTimeTask implements Comparable<MyTimeTask>{// 需要执行的任务private final Runnable task;// 记录任务要执行的 "时刻"private final long time;public MyTimeTask(Runnable task, long time) {this.task = task;this.time = time;}public long getTime() {return time;}public void run() {task.run();}@Overridepublic int compareTo(MyTimeTask o) {return (int)(this.time - o.time);}
}class MyTimer {PriorityQueue<MyTimeTask> queue = null;final Object locker = new Object();public MyTimer() {// 使用优先级队列(小根堆)来管理任务执行的顺序this.queue = new PriorityQueue<>();// 创建一个线程,负责执行队列中的任务Thread t = new Thread(() -> {try {while (true) {// 涉及多个线程,一定涉及线程安全问题synchronized (locker) {// 如果队列为空,那么让该线程等待,直到offer任务到队列中// 不推荐使用continue,避免反复快速执行while循环消耗不必要的资源while (queue.isEmpty()) {// continuelocker.wait();}MyTimeTask task = queue.peek();// 当前任务时间, 如果比系统时间大, 说明任务执行的时机未到if (System.currentTimeMillis() < task.getTime()) {// 避免使用 sleep 导致程序造成问题locker.wait( task.getTime() - System.currentTimeMillis());} else {// 时间到了,执行任务task.run();queue.poll();}}}} catch (InterruptedException e) {throw new RuntimeException();}});t.start();}public void schedule(Runnable task, long delay) {synchronized (locker) {MyTimeTask timeTask = new MyTimeTask(task, System.currentTimeMillis() + delay);queue.offer(timeTask);locker.notify();}}
}public class Demo36 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(() -> {System.out.println("3秒后执行");}, 3000);myTimer.schedule(() -> {System.out.println("2秒后执行");}, 2000);myTimer.schedule(() -> {System.out.println("1秒后执行");}, 1000);System.out.println("立即执行");}
}

· 上述代码中还有一些需要注意的点:

        1. 调用 schedule 的是一个线程,定时器内部又有一个线程执行任务逻辑,那么就涉及到多线程操作,所以就可能存在线程安全问题,所以需要对程序进行加锁

        2. ,这里的最初版本其实为:if (queue.isEmpty()) { continue; } 那为什么改了呢?如果单纯为 if 判断是否空,且 continue 到循环首行,这里确实需要等,但这里却依旧在让 CPU 消耗资源,但并没有起到实质性的作用,说白了就是空等,CPU空转;为了优化代码,将其改为上述最终版本,使用 wait 操作,便可让 CPU 暂时对这里释放资源~~~

        3. 这里可以换成 sleep 吗?当然不可以~;如果有一个程序允许时间为 11:00,有一个任务的运行时间为 11:30,此时 sleep 就会睡眠 30 分钟,若又有一个任务进来了,且它在 11:20 就要执行,但是 sleep 一旦触发,除非线程 Interrupt ,否则无法唤醒 sleep,那么就错过了后进来的任务的执行了,使用 wait,尽管是有参数版本的,依然可以被 notify 唤醒~~~

        4. 在任务量少的场景下,一个线程执行这些操作绰绰有余,但如果碰上了非常大量的任务场景,此时一个线程就有点吃不消了,所以还是建议结合线程池来使用定时器,以防不必要的事情发生~~~


时间轮实现定时器 

· 这里就是简单科普一下吧:交给 GPT 了~~~

设计思想

时间轮是一种环形数组结构,类似钟表的秒针。每个槽代表一个固定的时间单位(如 1 毫秒、1 秒等),每个槽可以存储多个定时任务。当时间推进时,指针在环上移动,指向当前时间对应的槽,并触发其中的任务。

执行流程

  1. 定义一个固定大小的环形数组(如 360 个槽,表示 360 秒)。
  2. 将定时任务根据触发时间计算分配到对应槽中:
    • 任务剩余时间 < 一圈:分配到当前圈;
    • 任务剩余时间 > 一圈:计圈数延迟,直到圈数耗尽。
  3. 定时器线程按固定频率移动指针,每次移动一格:
    • 执行指针所在槽中的任务;
    • 清空已完成任务。

优点

  • 高效性:插入和触发任务的时间复杂度为 O(1)。
  • 节省内存:通过时间轮的槽结构减少了对定时任务的单独存储。
  • 适合大规模任务:当任务数量极多,触发时间分布较广时,时间轮比优先级队列效率高。

缺点

  • 时间精度受限:时间粒度由槽的间隔决定,不能无限精确。
  • 复杂度较高:需要处理多圈任务,以及跨圈的边界情况。
  • 不适合稀疏任务:任务密集时表现优异,但当任务稀疏且跨度大时,可能浪费大量空槽。

适用场景

  • 大规模定时任务,任务时间跨度大且密集。
  • 时间精度要求不高,如网络超时检测、流量限速等。

形象比喻

时间轮像一个“时钟闹铃”:每个格子是一个时间点(比如 1 秒钟后或 2 分钟后),指针每到一个格子,就检查是否有任务需要执行。

· 希望各位大佬看懂了

· 那么我们下节再见吧~~~

· 毕竟不知后事如何,且听下回分解 

 

相关文章:

Java多线程详解①①(全程干货!!!) 实现简单的线程池 || 定时器 || 简单实现定时器 || 时间轮实现定时器

这里是Themberfue 上一节讲了 线程池 线程池中的拒绝策略 等相关内容 实现简单的线程池 一个线程池最核心的方法就是 submit&#xff0c;通过 submit 提交 Runnable 任务来通知线程池来执行 Runnable 任务 我们简单实现一个特定线程数量的线程池&#xff0c;这些线程应该在…...

DAMODEL丹摩|部署FLUX.1+ComfyUI实战教程

本文仅做测评体验&#xff0c;非广告。 文章目录 1. FLUX.1简介2. 实战2. 1 创建资源2. 1 ComfyUI的部署操作2. 3 部署FLUX.1 3. 测试5. 释放资源4. 结语 1. FLUX.1简介 FLUX.1是由黑森林实验室&#xff08;Black Forest Labs&#xff09;开发的开源AI图像生成模型。它拥有12…...

请求(request)

目录 前言 request概述 request的使用 获取前端传递的数据 实例 请求转发 特点 语法 实例 实例1 实例2 【关联实例1】 域对象 组成 作用范围&#xff1a; 生命周期&#xff1a; 使用场景&#xff1a; 使用步骤 存储数据对象 获得数据对象 移除域中的键值…...

关于VNC连接时自动断联的问题

在服务器端打开VNC Server的选项设置对话框&#xff0c;点左边的“Expert”&#xff08;专家&#xff09;&#xff0c;然后找到“IdleTimeout”&#xff0c;将数值设置为0&#xff0c;点OK关闭对话框。搞定。 注意,服务端有两个vnc服务,这俩都要设置ide timeout为0才行 附件是v…...

C语言strtok()函数用法详解!

strtok 是 C 标准库中的字符串分割函数&#xff0c;用于将一个字符串拆分成多个部分&#xff08;token&#xff09;&#xff0c;以某些字符&#xff08;称为分隔符&#xff09;为界限。 函数原型 char *strtok(char *str, const char *delim);参数&#xff1a; str&#xff1a…...

【docker 拉取镜像超时问题】

问题描述 在centosStream8上安装docker&#xff0c;使用命令sudo docker run hello-world 后出现以下错误&#xff1a; Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Ti…...

模拟手机办卡项目(移动大厅)--结合面向对象、JDBC、MYSQL、dao层模式,使用JAVA控制台实现

目录 1. 项目需求 2. 项目使用的技术 3.项目需求分析 3.1 实体类和接口 4.项目结构 5.业务实现 5.1 登录 5.1.1 实现步骤 5.1.2 原生代码问题 ​编辑 5.1.3 解决方法 1.说明&#xff1a; 2. ResultSetHandler结果集处理 5.1.4 代码 5.1.5 实现后的效果图 登录成功​…...

机器学习—大语言模型:推动AI新时代的引擎

云边有个稻草人-CSDN博客 目录 引言 一、大语言模型的基本原理 1. 什么是大语言模型&#xff1f; 2. Transformer 架构 3. 模型训练 二、大语言模型的应用场景 1. 文本生成 2. 问答系统 3. 编码助手 4. 多语言翻译 三、大语言模型的最新进展 1. GPT-4 2. 开源模型 …...

C++:探索哈希表秘密之哈希桶实现哈希

文章目录 前言一、链地址法概念二、哈希表扩容三、哈希桶插入逻辑四、析构函数五、删除逻辑六、查找七、链地址法代码实现总结 前言 前面我们用开放定址法代码实现了哈希表&#xff1a; C&#xff1a;揭秘哈希&#xff1a;提升查找效率的终极技巧_1 对于开放定址法来说&#…...

具身智能高校实训解决方案——从AI大模型+机器人到通用具身智能

一、 行业背景 在具身智能的发展历程中&#xff0c;AI 大模型的出现成为了关键的推动力量。这些大模型具有海量的参数和强大的语言理解、知识表示能力&#xff0c;能够为机器人的行为决策提供更丰富的信息和更智能的指导。然而&#xff0c;单纯的大模型在面对复杂多变的现实…...

【消息序列】详解(8):探秘物联网中设备广播服务

目录 一、概述 1.1. 定义与特点 1.2. 工作原理 1.3. 应用场景 1.4. 技术优势 二、截断寻呼&#xff08;Truncated Page&#xff09;流程 2.1. 截断寻呼的流程 2.2. 示例代码 2.3. 注意事项 三、无连接外围广播过程 3.1. 设备 A 启动无连接外围设备广播 3.2. 示例代…...

【RL Base】强化学习核心算法:深度Q网络(DQN)算法

&#x1f4e2;本篇文章是博主强化学习&#xff08;RL&#xff09;领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅…...

深入浅出 Python 网络爬虫:从零开始构建你的数据采集工具

在大数据时代&#xff0c;网络爬虫作为一种数据采集技术&#xff0c;已经成为开发者和数据分析师不可或缺的工具。Python 凭借其强大的生态和简单易用的语言特点&#xff0c;在爬虫领域大放异彩。本文将带你从零开始&#xff0c;逐步构建一个 Python 网络爬虫&#xff0c;解决实…...

美国发布《联邦风险和授权管理计划 (FedRAMP) 路线图 (2024-2025)》

文章目录 前言一、战略目标实施背景2010年12月&#xff0c;《改革联邦信息技术管理的25点实施计划》2011年2月&#xff0c;《联邦云计算战略》2011年12月&#xff0c;《关于“云计算环境中的信息系统安全授权”的首席信息官备忘录》2022年12月&#xff0c;《FedRAMP 授权法案》…...

Python语法基础(三)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 我们这篇文章来说一下函数的返回值和匿名函数 函数的返回值 我们先来看下面的这一段函数的定义代码 # 1、返回值的意义 def func1():print(111111111------start)num166print…...

云计算之elastaicsearch logstach kibana面试题

1.ELK是什么? ELK 其实并不是一款软件,而是一整套解决方案,是三个软件产品的首字母缩写 Elasticsearch:负责日志检索和储存 Logstash:负责日志的收集和分析、处理 Kibana:负责日志的可视化 这三款软件都是开源软件,通常是配合使用,而且又先后归于 Elastic.co 公司名下,…...

【已解决】git push需要输入用户名和密码问题

解决方法&#xff1a; 1&#xff09;查看使用的clone方式&#xff1a; git remote -v 2&#xff09;若为HTTPS&#xff0c;删除原clone方式: git remote rm origin 3&#xff09;添加新的clone方式&#xff1a; git remote add origin gitgithub.com:zludon/git_test.git …...

python的字符串处理

需求&#xff1a; 编写一个程序&#xff0c;输入一段英文句子&#xff0c;统计每个单词的长度&#xff0c;并将单词按照长度从短到长排序。 程序逻辑框图 1、用户输入一句英文句子。 2、对输入的句子进行预处理&#xff08;去空格并分割为单词列表&#xff09;。 3、统计每个单…...

【线程】Java多线程代码案例(2)

【线程】Java多线程代码案例&#xff08;2&#xff09; 一、定时器的实现1.1Java标准库定时器1.2 定时器的实现 二、线程池的实现2.1 线程池2.2 Java标准库中的线程池2.3 线程池的实现 一、定时器的实现 1.1Java标准库定时器 import java.util.Timer; import java.util.Timer…...

虚拟机之间复制文件

在防火墙关闭的前提下&#xff0c;您可以通过几种不同的方法将文件从一个虚拟机复制到另一个虚拟机。这里&#xff0c;我们假设您想要从 IP 地址为 192.168.4.5 的虚拟机上的 /tmp 文件夹复制文件到当前虚拟机&#xff08;192.168.4.6&#xff09;的 /tmp 文件夹下。以下是几种…...

如何为 XFS 文件系统的 /dev/centos/root 增加 800G 空间

如何为 XFS 文件系统的 /dev/centos/root 增加 800G 空间 一、前言二、准备工作三、扩展逻辑卷1. 检查现有 LVM 配置2. 扩展物理卷3. 扩展卷组4. 扩展逻辑卷四、调整文件系统大小1. 检查文件系统状态2. 扩展文件系统五、处理可能出现的问题1. 文件系统无法扩展2. 磁盘空间不足3…...

Java算法OJ(11)双指针练习

目录 1.前言 2.正文 2.1存在重复数字 2.1.1题目 2.1.2解法一代码 解析&#xff1a; 2.1.3解法二代码 解析&#xff1a; 2.2存在重复数字plus 2.2.1题目 2.2.2代码 2.2.3解析 3.小结 1.前言 哈喽大家好吖&#xff0c;今天来给大家分享双指针算法的相关练习&…...

44.扫雷第二部分、放置随机的雷,扫雷,炸死或成功 C语言

按照教程打完了。好几个bug都是自己打出来的。比如统计周围8个格子时&#xff0c;有一个各自加号填成了减号。我还以为平移了&#xff0c;一会显示是0一会显示是2。结果单纯的打错了。debug的时候断点放在scanf后面会顺畅一些。中间多放一些变量名方便监视。以及mine要多显示&a…...

大语言模型LLM的微调代码详解

代码的摘要说明 一、整体功能概述 这段 Python 代码主要实现了基于 Hugging Face Transformers 库对预训练语言模型&#xff08;具体为 TAIDE-LX-7B-Chat 模型&#xff09;进行微调&#xff08;Fine-tuning&#xff09;的功能&#xff0c;使其能更好地应用于生成唐诗相关内容的…...

钉钉与企业微信机器人:助力网站定时任务高效实现

钉钉、企业微信机器人在网站定时任务中的应用&#xff0c;主要体现在自动化通知、提醒以及数据处理等方面。 以下是一些具体的应用场景&#xff1a; 1. 自动化通知 项目进度提醒&#xff1a;在蒙特网站所负责的软件开发或网站建设项目中&#xff0c;可以利用机器人设置定时任…...

自然语言处理工具-广告配音工具用于语音合成助手/自媒体配音/广告配音/文本朗读-已经解锁了 全功能的 apk包

Android -「安卓端」 广告配音工具用于语音合成助手/自媒体配音/广告配音/文本朗读。 广告配音工具&#xff1a;让您的文字“说话”&#xff0c;在这个快速发展的数字时代&#xff0c;广告配音工具为各种语音合成需求提供了一站式解决方案。无论是自媒体配音、商业广告配音、…...

深入解析注意力机制

引言随着深度学习的快速发展&#xff0c;注意力机制&#xff08;Attention Mechanism&#xff09;逐渐成为许多领域的关键技术&#xff0c;尤其是在自然语言处理&#xff08;NLP&#xff09;和计算机视觉&#xff08;CV&#xff09;中。其核心思想是赋予模型“关注重点”的能力…...

Unity图形学之雾Fog

1.设置雾化&#xff1a; 2.雾化变化曲线&#xff1a;FogMode &#xff08;1&#xff09;线性&#xff1a; &#xff08;2&#xff09;一次指数&#xff1a; &#xff08;3&#xff09;二次指数&#xff1a; Shader "Custom/FogTest" {Properties{_Color ("Color…...

【大数据学习 | Spark-Core】详解Spark的Shuffle阶段

1. shuffle前言 对spark任务划分阶段&#xff0c;遇到宽依赖会断开&#xff0c;所以在stage 与 stage 之间会产生shuffle&#xff0c;大多数Spark作业的性能主要就是消耗在了shuffle环节&#xff0c;因为该环节包含了大量的磁盘IO、序列化、网络数据传输等操作。 负责shuffle…...

如何启动 Docker 服务:全面指南

如何启动 Docker 服务:全面指南 一、Linux 系统(以 Ubuntu 为例)二、Windows 系统(以 Docker Desktop 为例)三、macOS 系统(以 Docker Desktop for Mac 为例)四、故障排查五、总结Docker,作为一种轻量级的虚拟化技术,已经成为开发者和运维人员不可或缺的工具。它允许用…...

开原 铁岭网站建设/网络营销策划书包括哪些内容

原文地址&#xff1a;你必须知道的地理坐标系和投影坐标系 文章条理清晰&#xff0c;内容通俗易懂 还可以参考另一篇文章&#xff1a;GISer梳理的我国常用的坐标系及相关知识 1、基本概念 地理坐标系&#xff1a;为球面坐标。 参考平面地是椭球面&#xff0c;坐标单位&#xff…...

红色旅游网站页面建设/百度手机助手网页

(1&#xff09;将各个列表组合成一个新列表&#xff0c;不做任何数据的改变 示例&#xff1a; test1 [1,2] test2 [1,3] test3 [1,4] 要求生成新的结果&#xff1a;test [[1,2],[1,3],[1,4]] 代码示例&#xff1a; test1 [1,2] test2 [1,3] test3 [1,4] multi_listmap(lis…...

网站建设企业所得税/成都网站制作费用

服务器vps分销 内容精选换一换oVirt Engine是运行在基于JBoss的Java应用程序。该服务与主机上的VDSM进行通信&#xff0c;以部署、启动、停止、迁移和监控VM&#xff0c;并且还可以通过模板在存储上创建新的镜像。oVirt Engine以可扩展性、安全性、高性能为基础的架构技术&…...

最专业的企业营销型网站建设价格/seo公司软件

千呼万唤始出来&#xff01;为了更好的迎接2021年&#xff0c;小编特汇总了四大运营商发布的5G资料供大家学习&#xff0c;欢迎下载。小编共汇总了94份资料&#xff0c;中国移动有45份&#xff0c;中国联通有28份&#xff0c;中国电信有15份&#xff0c;中国广电有6份。文件获取…...

天猫网站怎么做/宁波seo网站推广

Offer_day03_58 - II. 左旋转字符串 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如&#xff0c;输入字符串"abcdefg"和数字2&#xff0c;该函数将返回左旋转两位得到的结果"cdefgab"…...

百度网盟 网站定向/企业网站建设专业服务

HashMap源码解析老样子话不多说先上一张UML类图看一下里面的方法&#xff0c;我们抽重点的和常用的讲HashMap的构造方法HashMap()方法//负载因子final float loadFactor;//默认负载因子为0.75static final float DEFAULT_LOAD_FACTOR 0.75f;public HashMap() {this.loadFactor…...