ScheduledThreadPoolExecutor和时间轮算法比较

最近项目中需要用到超时操作,对于不是特别优秀的timer和DelayQueue没有看。

  • Timer 是单线程模式。如果某个 TimerTask 执行时间很久,会影响其他任务的调度。
  • Timer 的任务调度是基于系统绝对时间的,如果系统时间不正确,可能会出现问题。
  • TimerTask 如果执行出现异常,Timer 并不会捕获,会导致线程终止,其他任务永远不会执行。
  • 相比于 Timer,DelayQueue 只实现了任务管理的功能,需要与异步线程配合使用。

着重看了一下ScheduledThreadPoolExecutor和时间轮算法,以下一些大致讲解和我的理解。

ScheduledThreadPoolExecutor

前置知识

继承于ThreadPoolExecutor,也就是说调用线程执行任务的流程是一样的,详见ensurePrestart();方法。

在ScheduledThreadPoolExecutor中使用的阻塞队列是DelayedWorkQueue,一个自定义的类。内部使用以下queue存放定时任务。

private RunnableScheduledFuture<?>[] queue =new RunnableScheduledFuture<?>[INITIAL_CAPACITY];

上边提到的这个queue,是一个模拟优先级队列的堆(定时任务类实现了compareTo的方法),

    public int compareTo(Delayed other) {if (other == this) // compare zero if same objectreturn 0;if (other instanceof ScheduledFutureTask) {ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;long diff = time - x.time;if (diff < 0)return -1;else if (diff > 0)return 1;else if (sequenceNumber < x.sequenceNumber)return -1;elsereturn 1;}long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;}

查询时间复杂度log(n),可以从siftUp方法看出来是以何种规则把任务存放到数组。

大致流程

创建一个定时任务

把任务放在优先级阻塞队列

新增worker(ThreadPoolExecutor的流程)从阻塞队列拿出任务

判断任务时间(任务中会有预期执行时间,时间不到则调用available.awaitNanos(delay);阻塞)

由于是优先级队列,所以都从queue[0]去取任务,一个线程阻塞,其他线程available.await();也阻塞

具体代码:

    public RunnableScheduledFuture<?> take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {for (;;) {RunnableScheduledFuture<?> first = queue[0];if (first == null)available.await();else {long delay = first.getDelay(NANOSECONDS);if (delay <= 0L)return finishPoll(first);first = null; // don't retain ref while waitingif (leader != null)available.await();else {Thread thisThread = Thread.currentThread();leader = thisThread;try {available.awaitNanos(delay);} finally {if (leader == thisThread)leader = null;}}}}} finally {if (leader == null && queue[0] != null)available.signal();lock.unlock();}}private RunnableScheduledFuture<?> finishPoll(RunnableScheduledFuture<?> f) {int s = --size;RunnableScheduledFuture<?> x = queue[s];queue[s] = null;if (s != 0)siftDown(0, x);setIndex(f, -1);return f;}public RunnableScheduledFuture<?> poll() {final ReentrantLock lock = this.lock;lock.lock();try {RunnableScheduledFuture<?> first = queue[0];return (first == null || first.getDelay(NANOSECONDS) > 0)? null: finishPoll(first);} finally {lock.unlock();}}

 个人看法

面对海量任务插入和删除的场景,会遇到比较严重的性能瓶颈

原因有以下两方面:

1. 自定义的阻塞队列queue不能自定义初始容量,只有16,需要考虑是否会频繁扩容

2. 就是上方提到的以下两个方法会导致 DelayedWorkQueue 数据结构频繁发生变化

    private void siftUp(int k, RunnableScheduledFuture<?> key) {while (k > 0) {int parent = (k - 1) >>> 1;RunnableScheduledFuture<?> e = queue[parent];if (key.compareTo(e) >= 0)break;queue[k] = e;setIndex(e, k);k = parent;}queue[k] = key;setIndex(key, k);}/*** Sifts element added at top down to its heap-ordered spot.* Call only when holding lock.*/private void siftDown(int k, RunnableScheduledFuture<?> key) {int half = size >>> 1;while (k < half) {int child = (k << 1) + 1;RunnableScheduledFuture<?> c = queue[child];int right = child + 1;if (right < size && c.compareTo(queue[right]) > 0)c = queue[child = right];if (key.compareTo(c) <= 0)break;queue[k] = c;setIndex(c, k);k = child;}queue[k] = key;setIndex(key, k);}

时间轮算法

原理

原理很简单,一张图直接概括(偷来的图,下图是netty的实现),只看原理不看类名

时间的维度是无限的,但是钟表却能表示无限的时间。所以这个时间轮算法和钟表相似都可以表示无限的时间,只要把圈数和槽位记录好即可。

需要注意的是,时间轮算法虽然看起来是物理上的⚪,但其实只是逻辑上的回环,走到末尾时重新回到头。

类比钟表,时间轮上也有一个个的插槽(slot),一个插槽代表的是时间间隔(interval),时间间隔越小,时间表示越精确。

大致流程

放入定时任务

封装任务记录剩余圈数,槽位等等

扫描槽位以及链表,扫描到对应槽位找到剩余圈数为0的执行

个人看法,因为这只是算法思想,所以以下不是缺陷而是需要注意的点

  • 如果长时间没有到期任务,那么会存在时间轮空推进的现象。
  • 只适用于处理耗时较短的任务,由于 Worker 是单线程的,如果一个任务执行的时间过长,会造成 Worker 线程阻塞。
  • 相比传统定时器的实现方式,内存占用较大。

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

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

相关文章

STL中的迭代器模式:将算法与数据结构分离

目录 1.概述 2.容器类 2.1.序列容器 2.2.关联容器 2.3.容器适配器 2.4.数组 3.迭代器 4.重用标准迭代器 5.总结 1.概述 在之前&#xff0c;我们讲了迭代器设计模式&#xff0c;分析了它的结构、角色以及优缺点&#xff1a; 设计模式之迭代器模式-CSDN博客 在 STL 中&a…

Open AI限制来袭?用上这个工具轻松破局!

【导语】近日&#xff0c;AI领域掀起了一场不小的波澜。Open AI宣布&#xff0c;从7月9日起&#xff0c;将对部分地区的开发者实施API调用限制。这一消息对于许多依赖Open AI技术的国内初创团队来说&#xff0c;无疑是一个沉重的打击。 对于这些团队而言&#xff0c;Open AI的A…

关于摄像头模组中滤光片的介绍

1、问题背景 红外截止滤光片&#xff08;IR CUT Filter&#xff09;是应用在摄像头模组中非常重要的一个器件&#xff0c;因人眼与 coms sensor 对光线各波长的响应不同&#xff0c; 人眼看不到红外光&#xff0c;但 sensor 能感应到&#xff08;如下图是某sensor在各波长下的…

使用 SwiftUI 为 macOS 创建类似于 App Store Connect 的选择器

文章目录 前言创建选择器组件使用选择器组件总结前言 最近,我一直在为我的应用开发一个全新的界面,它可以让你查看 TestFlight 上所有可用的构建,并允许你将它们添加到测试群组中。 作为这项工作的一部分,我需要创建一个组件,允许用户从特定构建中添加和删除测试群组。我…

Flutter学习目录

学习Dart语言 官网&#xff1a;https://dart.cn/ 快速入门&#xff1a;Dart 语言开发文档&#xff08;dart.cn/guides&#xff09; 学习Flutter Flutter生命周期 点击跳转Flutter更换主题 点击跳转StatelessWidget和StatefulWidget的区别 点击跳转学习Flutter中新的Navigato…

Linux操作系统通过实战理解CPU上下文切换

前言&#xff1a;Linux是一个多任务的操作系统&#xff0c;可以支持远大于CPU数量的任务同时运行&#xff0c;但是我们都知道这其实是一个错觉&#xff0c;真正是系统在很短的时间内将CPU轮流分配给各个进程&#xff0c;给用户造成多任务同时运行的错觉。所以这就是有一个问题&…

个人网站搭建-步骤(持续更新)

域名申请 域名备案 域名解析 服务器购买 端口转发 Nginx要在Linux上配置Nginx进行接口转发&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装Nginx&#xff08;如果尚未安装&#xff09;&#xff1a; 使用包管理工具&#xff08;如apt, yum, dnf, 或zypper&#x…

高考志愿不知道怎么填?教你1招,用这款AI工具,立省4位数

高中的岁月&#xff0c;就像一本厚厚的书&#xff0c;我们一页页翻过&#xff0c;现在&#xff0c;终于翻到了最后一页。但这不是结束&#xff0c;这是新的开始&#xff0c;是人生的新篇章。 高考落幕&#xff0c;学子们在短暂的放松后&#xff0c;又迎来了紧张的志愿填报。 “…

使用bootstrap框架做一个Aotm Blog个人博客

使用bootstrap框架做一个Aotm Blog个人博客&#xff0c;展示一些自己的个人信息&#xff0c;有四个博客分类&#xff1a;心情记录、学习笔记、旅行相册、美食打卡。 主界面图&#xff1a; 心情记录界面 学习笔记界面&#xff1a; 旅行相册界面&#xff1a; 美食打卡界面&#…

深入探索:大型语言模型消除幻觉的解决之道

随着人工智能技术的飞速发展&#xff0c;大型语言模型&#xff08;LLMs&#xff09;已经成为自然语言处理领域的明星。它们以其庞大的知识库和生成连贯、上下文相关文本的能力&#xff0c;极大地推动了研究、工业和社会的进步。然而&#xff0c;这些模型在生成文本时可能会产生…

Unity保存玩家的数据到文件中(Unity的二进制序列化)

文章目录 文章运行环境什么是二进制序列化读写文件构造函数 自定义二进制序列化 文章运行环境 Unity2022 什么是二进制序列化 Unity中的二进制序列化是一种将游戏对象或数据结构转换为二进制格式的过程&#xff0c;以便于存储或网络传输。这使数据能够以高效的方式保存&…

富唯智能推出的AMR复合机器人铝板CNC上下料方案

随着科技的不断进步&#xff0c;CNC加工行业正面临着前所未有的变革。传统的CNC上下料方式已无法满足现代生产对效率、精度和安全性的高要求。在这样的背景下&#xff0c;富唯智能推出的AMR复合机器人铝板CNC上下料方案&#xff0c;以其智能化、自动化的特点&#xff0c;引领了…

Mind+在线图形编程软件(Sractch类软件)

Scratch作为图形编程软件&#xff0c;可以为小朋友学习编程提供很好的入门&#xff0c;是初次接触编程的小朋友的首选开发软件。这里介绍的Mind软件与Sractch用法几乎完全一致&#xff0c;并且可以提供在线免安装版本使用&#xff0c;浏览器直接打开网址&#xff1a; ide.mindp…

LeetCode.32最长有效括号详解

问题描述 给你一个只包含 ( 和 ) 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 解题思路1 有效的括号字符串意味着每一个左括号 ( 都可以找到一个相匹配的右括号 )。栈可以帮助我们追踪尚未匹配的括号&#xff0c;并有效地处理…

Micro-ROS是什么?

Micro-ROS是ROS&#xff08;Robot Operating System&#xff0c;机器人操作系统&#xff09;生态系统的一个重要组成部分&#xff0c;专为微控制器&#xff08;Microcontrollers&#xff09;设计的轻量级ROS版本。它的目标是在资源有限的嵌入式平台上实现ROS 2的功能&#xff0…

RocketMQ:日常开发中有哪些使用MQ的场景

什么是消息队列&#xff1f; 消息队列是一种通信方法&#xff0c;允许应用程序通过发送和接收消息来互相通信。这些消息/任务/指令存储在一个中间介质中&#xff08;即队列&#xff09;&#xff0c;并由生产者发送&#xff0c;消费者接收。 使用场景 场景一&#xff1a;任务…

昇思25天学习打卡营第1天|基本介绍

MindSpore 基本介绍设计理念层次结构 基本介绍 昇思MindSpore是一个全场景深度学习框架&#xff0c;旨在实现易开发、高效执行、全场景统一部署三大目标。 易开发&#xff1a;API友好、调试难度低高效执行&#xff1a;包括计算效率、数据预处理效率和分布式训练效率全场景&am…

手写SpringMVC之ApplicationContextListener

什么是Spring MVC&#xff1f; Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称&#xff08; spring-webmvc &#xff09;&#xff0c;但它通常被称为“Spring MVC”。 手写…

【python】OpenCV—Color Map

文章目录 cv2.applyColorMapcv2.putText小试牛刀自定义颜色 参考学习来自 OpenCV基础&#xff08;21&#xff09;使用 OpenCV 中的applyColorMap实现伪着色 cv2.applyColorMap cv2.applyColorMap() 是 OpenCV 中的一个函数&#xff0c;用于将灰度图像或单通道图像应用一个颜色…

The First Descendant卡顿怎么办?快速处理第一后裔卡顿

第一后裔/The First Descendant是一款刷宝类RPG射击游戏&#xff0c;以虚幻引擎5为基础&#xff0c;使其对决场景十分精彩、刺激&#xff0c;从而吸引了大批冒险者前往&#xff0c;这里揭开属于英格里斯和继承者的秘密。不过有很多玩家&#xff0c;反馈在对局时遇到了卡顿、画面…