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

【技术选型】Java 定时任务

文章目录

  • 背景
  • 一、基础
    • 1.1 Cron表达式
    • 1.2 定时任务的三大组成部分
  • 二、Java做定时任务的技术方案比较
    • 2.1、JDK seelp实现定时任务
    • 2.2、JDK Timer & TimerTask 实现定时任务
    • 2.3、JDK ScheduledExecutorService
    • 2.4、Quartz框架
    • 2.5、Spring Task 中的 @schedule
    • 2.6、Elastic-Job框架
    • 2.7、PowerJob框架
    • 2.8、Xxl-job
  • 三、共同点和不同点
  • 总结

背景

在日常的开发工作中我们经常会遇到定时任务的相关问题,比如:

  • 信用卡定时每月给用户推送账单数据;
  • 轮训更新某个任务的状态是否完成;
  • 设置一个定时提醒;
  • 邮件或消息设置定时发送;
  • 定时统计某个时间段的数据存入缓存;

等等,定时可以算一个最常用的开发工作,你日常的工作中肯定也写了不少的定时任务代码,但是你真的了解定时任务吗?

下面让我们一起进入定时任务的学习!

一、基础

1.1 Cron表达式

cron是当前做定时任务的基础,即使很多人说cron表达式不容易理解,但这是现在阶段所有程序编写定时任务的基础和唯一选择。

就算做不到熟练编写,也应该做到看到能懂;

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义

接下来具体看cron属性对应时间表达式的定义规则:

  1. 按顺序依次是:秒、分、时、日、月、周,中间用空格间隔
  2. 月、周可以用数字或英文单词的前三个字母表示
  3. 日和周可能会冲突,因此两个可以有一个配置为?
  4. 常用通配符的含义:
    • *表示任意值,例如在秒字段上设置*,表示每秒都触发
    • ?表示不指定值,只能出现在日或者周的位置,用于处理日和周可能存在的冲突,例如2020年8月15是周六,如果又在周的位置上指定为周一,那就会产生冲突到导致定时任务失效。如果我们不关心日或者周的时候,也可以将其设置为?
    • -表示时间区间,例如在秒上设置1-3,表示第1、2、3秒都会触发
    • /表示时间间隔,例如在秒上设置2/4,表示从第2秒开始每间隔4秒触发一次
    • ,表示列举多个值,例如MON,WED,FRI表示周一、周三、周五触发

例如:

  • 每隔5秒执行一次:*/5 * * * * ?
  • 每隔1分钟执行一次:0 */1 * * * ?
  • 每天23点执行一次:0 0 23 * * ?
  • 每天凌晨1点执行一次:0 0 1 * * ?
  • 每月1号凌晨1点执行一次:0 0 1 1 * ?
  • 每月最后一天23点执行一次:0 0 23 L * ?
  • 每周星期天凌晨1点实行一次:0 0 1 ? * L
  • 在26分、29分、33分执行一次:0 26,29,33 * * * ?
  • 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?

1.2 定时任务的三大组成部分

调度器Scheduler、执行器 executors、触发器 Trigger

不管你使用的什么框架、用的是什么系统,或者将来又出现什么新的定时任务技术,都离不开这三部分。

我们以一个闹钟响铃的任务为例:

  • 执行器executors:发出一阵刺耳的声音;(具体的执行操作)
  • 触发器Trigger:发出声音的具体时间; (触发任务执行的规则,多为时间规则。)
  • 调度器Scheduler:一直运行到触发时间点发出刺耳的声音;(进行任务的调度)

所以,当接手一个新的定时任务的框架,首先要看其这三部分是这么去实现的;

二、Java做定时任务的技术方案比较

Java做定时任务的技术方案有多种,本文将比较常见的技术方案,分析它们的优缺点,以便开发人员在实际项目中选择合适的技术方案。

2.1、JDK seelp实现定时任务

  • 使用Thread.sleep()方法实现定时任务。可以在独立的线程中执行任务,并在每次任务完成后使用Thread.sleep()方法让线程休眠一段时间。
  • 下面是一个示例,该示例定义了一个任务,该任务每隔1秒打印“hello timer”:
public class TimerExample {public static void main(String[] args) { Runnable task = new Runnable() { public void run() { while (true) { System.out.println("hello timer"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }; Thread thread = new Thread(task); thread.start(); } 
}
  • 请注意,在本例中,任务是在无限循环中执行的,并且每次任务完成后线程都会休眠1秒。如果想要执行特定次数的定时任务,可以在任务内部使用计数器并在达到特定次数后终止循环。

2.2、JDK Timer & TimerTask 实现定时任务

使用Java的java.util.Timer和java.util.TimerTask类来创建定时任务。

  • 下面是一个简单的例子,其中定义了一个任务,该任务每隔1秒执行一次:

    import java.util.Timer; 
    import java.util.TimerTask; public class TimerExample { public static void main(String[] args) { TimerTask task = new TimerTask() { public void run() { System.out.println("Task executed"); } }; Timer timer = new Timer(); long delay = 1000; long interval = 1000; timer.scheduleAtFixedRate(task, delay, interval); } 
    }
    
  • 在上面的代码中,创建了一个TimerTask对象,该对象的run方法定义了要执行的任务。然后,使用Timer类创建了一个定时器,并使用scheduleAtFixedRate方法指定了任务的执行频率(即每隔1秒执行一次)。

    请注意,java.util.Timer是非线程安全的,因此在多线程环境中使用时需要注意。

    如果需要更复杂的定时任务,请考虑使用其他库,例如java.util.concurrent中的ScheduledExecutorService

  • 在Java 5之前,它是唯一的定时任务工具,在Java 5之后,JDK提供了更好的替代方案。

    • 优点:

      • 轻量级,使用简单。
      • 可以实现较为简单的定时任务功能。
    • 缺点:

      • 不支持复杂的定时任务类型,如cron表达式
      • 不支持任务持久化
      • 在定时任务数量很多的情况下,性能可能存在问题
      • 不支持分布式

2.3、JDK ScheduledExecutorService

  • ScheduledExecutorService是Java 5新增的一个接口,它位于java.util.concurrent包内,可以通过ThreadPoolExecutor来实现。可以使用它创建和执行定期执行的任务,并且可以控制任务的执行频率。
    它可以很方便地实现定时任务,支持多种执行方式,比如单次定时、固定频率定时和固定延迟时间定时等。

  • 以下是一个示例,该示例每隔1秒钟打印“hello timer”:

import java.util.concurrent.Executors; 
import java.util.concurrent.ScheduledExecutorService; 
import java.util.concurrent.TimeUnit; 
public class ScheduledExecutorServiceExample { public static void main(String[] args) { ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); Runnable task = new Runnable() { public void run() { System.out.println("hello timer");} }; executor.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS); } 
}
  • 在本例中,使用Executors类的newScheduledThreadPool方法创建了一个ScheduledExecutorService对象,并使用该对象的scheduleAtFixedRate方法将任务安排在每隔1秒钟执行一次。可以根据需要更改任务的执行频率。

  • 优点:

    • 简单易用,可灵活配置。
    • 支持多种定时任务类型,灵活度高。
    • 支持并发调度,可以提高性能。
  • 缺点:

    • 不支持分布式
    • 任务执行结果管理不够方便

2.4、Quartz框架

  • Quartz是一个流行的开源的定时任务框架,支持非常丰富的定时任务类型,比如简单定时任务、复杂定时任务、集群定时任务、任务持久化等。

  • 以下是一个使用Quartz创建定时任务的示例:

    import org.quartz.*; 
    import org.quartz.impl.StdSchedulerFactory; public class QuartzExample { public static void main(String[] args) throws SchedulerException { JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("myJob", "group1").build(); Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("myTrigger", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1) .repeatForever()) .build(); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.start();scheduler.scheduleJob(job, trigger); } 
    }public class HelloJob implements Job {public void execute(JobExecutionContext context) {System.out.println("hello timer");} 
    }
    
  • 在本例中,创建了一个名为HelloJob的作业类,该类实现了Quartz的Job接口。
    还创建了一个作业详细信息对象和一个触发器对象,并使用触发器安排了每隔1秒钟执行一次的任务。最后,启动了调度程序并使用其scheduleJob方法将任务与触发器关联。

  • 优点:

    • 功能丰富,支持多种定时任务类型。
    • 支持任务持久化,保证任务不会丢失。
    • 支持分布式,可以实现任务的高可用和负载均衡。
  • 缺点:

    • 依赖比较大,使用起来比较复杂。
    • 配置较为繁琐,需要考虑集群和持久化等问题。

2.5、Spring Task 中的 @schedule

  • Spring Task是Spring框架提供的一个模块,可以用来进行定时任务的管理。通过@Scheduled注解,可以实现简单的定时任务设置。同时也支持通过cron表达式设置复杂的定时任务。

  • 以下是一个示例,该示例每隔1秒钟打印“hello timer”:

    import org.springframework.scheduling.annotation.Scheduled; public class ScheduledAnnotationExample { @Scheduled(fixedRate = 1000) public void printMessage() { System.out.println("hello timer"); } 
    }
    
  • 在本例中,使用@Scheduled注解将任务标记为定时任务,并使用fixedRate属性设置任务的执行频率,即每隔1秒钟执行一次。

  • 优点:

    • 集成简单,开发便捷。
    • 支持使用注解来配置定时任务,使得代码更加易读易维护。
  • 缺点:

    • 不支持分布式
    • 不支持任务持久化

2.6、Elastic-Job框架

  • Elastic-Job是一个分布式作业调度框架,提供了高可用性、高扩展性等功能,可以用于创建分布式定时任务。

  • 以下是使用Elastic-Job创建一个简单的定时任务的示例:

    import com.dangdang.ddframe.job.api.ShardingContext; 
    import com.dangdang.ddframe.job.api.simple.SimpleJob; 
    public class MyJob implements SimpleJob { @Override public void execute(ShardingContext shardingContext) { System.out.println("Hello, world!"); } 
    }
    
  • 在本例中,创建了一个名为MyJob的作业类,该类实现了Elastic-Job的SimpleJob接口。可以在execute方法中编写定时执行的代码。还需要创建一个作业配置文件,该文件配置了任务的定时时间和任务的执行策略。

2.7、PowerJob框架

  • PowerJob是一种使用Java语言编写的分布式任务调度系统,可以用于创建定时任务。

  • 以下是使用PowerJob创建一个简单的定时任务的示例:

    import com.dangdang.ddframe.job.api.ShardingContext; 
    import com.dangdang.ddframe.job.api.simple.SimpleJob; 
    import org.slf4j.Logger; 
    import org.slf4j.LoggerFactory; public class MyJob implements SimpleJob { private static final Logger logger = LoggerFactory.getLogger(MyJob.class); @Override public void execute(ShardingContext shardingContext) { logger.info("Hello, world!"); } 
    }
    
  • 在本例中,创建了一个名为MyJob的简单作业类,该类实现了PowerJob的SimpleJob接口。可以在execute方法中编写定时执行的代码。

2.8、Xxl-job

  • Xxl-job是一个Java分布式定时任务解决方案,提供了高可用性、高扩展性等功能,可以用于创建分布式定时任务。可以用来代替Quartz。它支持类似cron表达式的定时任务设置,支持多种执行器类型,比如本地任务、Shell任务、Http任务等。同时还支持任务执行结果的管理和调度中心的高可用等特性。

  • 以下是使用XXL-JOB创建一个简单的定时任务的示例:

    import com.xxl.job.core.biz.model.ReturnT; 
    import com.xxl.job.core.handler.IJobHandler; 
    import com.xxl.job.core.handler.annotation.JobHandler; 
    import com.xxl.job.core.log.XxlJobLogger; 
    import org.springframework.stereotype.Component; 
    @JobHandler(value="myJobHandler") 
    @Component 
    public class MyJobHandler extends IJobHandler { @Override public ReturnT<String> execute(String param) throws Exception { XxlJobLogger.log("Hello, world!"); return SUCCESS; } 
    }  
    
  • 在本例中,创建了一个名为MyJobHandler的作业类,该类继承了XXL-JOB的IJobHandler类。可以在execute方法中编写定时执行的代码。
    还需要通过注解@JobHandler声明作业处理器名称,并在作业配置中将作业处理器与作业关联。

  • 优点:

    • 操作简单,集成容易。
    • 多种任务执行器类型,支持更多的任务类型。
    • 支持可视化的调度中心,调度管理更加方便。
  • 缺点:

    • 比较适合定时任务数量较多,任务类型较为丰富的情况,对于简单的定时任务使用稍显繁琐。

三、共同点和不同点

技术方案共同点不同点
Thread.sleep()简单易用不支持复杂的定时任务类型,不支持分布式
Timer类简单易用,轻量级不支持复杂的定时任务类型,不支持分布式
ScheduledExecutorService简单易用,支持多种定时任务类型,可灵活配置,支持并发调度不支持分布式,任务执行结果管理不够方便
Quartz框架支持多种定时任务类型,支持分布式、任务持久化配置较为繁琐,依赖比较大
Elastic-Job框架支持多种定时任务类型,支持分布式、任务持久化配置较为复杂
PowerJob框架支持多种定时任务类型,支持分布式、任务持久化po配置较为繁琐
Spring Task集成简单,支持注解配置不支持分布式,不支持任务持久化
Xxl-job操作简单,支持多种任务执行器类型,支持可视化的调度中心成就管理比较适合定时任务数量较多,任务类型较为丰富的情况

总结

总的来说,各个技术方案都有它们的优缺点,根据实际业务需求、技术需求和团队技术状况等,选择合适的技术方案是非常重要的。

相关文章:

【技术选型】Java 定时任务

文章目录 背景一、基础1.1 Cron表达式1.2 定时任务的三大组成部分 二、Java做定时任务的技术方案比较2.1、JDK seelp实现定时任务2.2、JDK Timer & TimerTask 实现定时任务2.3、JDK ScheduledExecutorService2.4、Quartz框架2.5、Spring Task 中的 schedule2.6、Elastic-Jo…...

让你立刻学会指针

☃️个人主页&#xff1a;fighting小泽 &#x1f338;作者简介&#xff1a;目前正在学习C语言和数据结构 &#x1f33c;博客专栏&#xff1a;C语言学习 &#x1f3f5;️欢迎关注&#xff1a;评论&#x1f44a;&#x1f3fb;点赞&#x1f44d;&#x1f3fb;留言&#x1f4aa;&am…...

重塑元宇宙体验!元宇宙实时云渲染解决方案来了

元宇宙作为人工智能、云计算和数字孪生等前沿技术的结合体&#xff0c;近年来越发受到各大企业重视。 元宇宙的应用场景层出不穷&#xff0c;不仅包括营销推广场景&#xff0c;还有品牌活动和电商销售&#xff0c;能有效提升品宣和商业转化效果。 元宇宙也具有极大的建设价值…...

Node【Global全局对象】

文章目录 &#x1f31f;前言&#x1f31f;Global全局对象&#x1f31f;Global对象属性与方法&#x1f31f;Global对象属性&#x1f31f;process&#x1f31f;Buffer类&#x1f31f;console &#x1f31f;写在最后 &#x1f31f;前言 哈喽小伙伴们&#xff0c;新的专栏 Node 已…...

【技术】《Netty》从零开始学netty源码(四十一)之PoolChunk

PoolChunk 我们再回顾以下netty中与内存相关的类&#xff1a; 前面我们已经分析了PoolSubpag&#xff0c;本章我们分析PoolChunk,先看下它的属性值&#xff1a; 为了更好的理解这些属性值&#xff0c;我们结合它的构造函数来理解&#xff0c;具体的源码如下&#xff1a; 其…...

新建虚拟机更改ip(连接xshell)

# 查看网络设备 [rootcentos79 ~]# nmcli device DEVICE TYPE STATE CONNECTION ens32 ethernet 已连接 ens32 ens33 ethernet 已连接 ens33 virbr0 bridge 已连接 virbr0 lo loopback 未托管 -- # 查看…...

什么是VBST和PVST?两者有啥区别?

在计算机网络中&#xff0c;VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;是一种将局域网划分为多个逻辑上独立的子网的技术&#xff0c;它可以帮助网络管理员更好地管理网络资源。 在VLAN技术中&#xff0c;STP&#xff08;Spanning Tree P…...

记录-JavaScript常规加密技术

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 当今Web开发中&#xff0c;数据安全是一个至关重要的问题&#xff0c;为了确保数据的安全性&#xff0c;我们需要使用加密技术。JavaScript作为一种客户端编程语言&#xff0c;可以很好地为数据进行加…...

二十三、高级网络技术及应用——BFD解析

文章目录 前言一、BFD 简介1、概述&#xff1a;2、作用&#xff1a; 二、静态路由调用 BFD1、配置静态 BFD2、配置动态 BFD 三、OSPF联动BFD四、BFD 单臂回声&#xff08;one arm echo&#xff09; 前言 BFD&#xff1a;Bidirectional Forwarding Detection&#xff0c;双向转…...

大家经常说的java八股文到底是什么?让我来总结一下吧!

八股文问题集合 面试必看java八股文 问题正在收录中&#xff0c;累了休息一会&#xff0c;如果有需要请&#xff0c;先关注&#xff0c;这几天会更帖子&#xff0c;答案后续补上 文章目录 八股文问题集合问题正在收录中&#xff0c;累了休息一会&#xff0c;如果有需要请&#…...

C++备忘录模式实践:轻松实现撤销与恢复功能

目录标题 引言&#xff08;Introduction&#xff09;备忘录模式定义及核心概念&#xff08;Memento Pattern Definition and Core Concepts&#xff09;备忘录模式的定义&#xff08;Definition of Memento pattern&#xff09;备忘录模式的主要角色&#xff08;Key roles in M…...

如何选择CDN加速平台?

现如今全球CDN市场规模逐年攀升&#xff0c;在2017年全球CDN市场规模约为75亿美元,到2021年增长到200亿美元左右。我国CDN行业同样保持高速发展,自2017年的135亿元增长到2022年的300亿元左右。但是国内的CDN市场规模仅为全球市场的15%-20%&#xff0c;海外CDN市场空间巨大。 接…...

其实苹果知道自己离不开中国制造,因此悄悄给自己留了后路

苹果在加速离开中国&#xff0c;不过从苹果的做法却又可以看到它其实很清醒地认识到无法离开中国制造&#xff0c;因此它在力推印度制造的时候&#xff0c;其实并没拼尽全力&#xff0c;深刻认识到印度制造和印度市场与中国的差距。 一、印度制造和印度市场与中国的差距 2022年…...

必用WhatsApp营销的4个理由

WhatsApp是世界上最受欢迎的消息传递应用程序。每天有1万新用户加入WhatsApp。各种规模的公司都利用该平台与世界各地的客户进行有效的沟通&#xff0c;这要归功于其广泛的覆盖范围、用户友好的设计和安全的端到端加密。因此&#xff0c;WhatsApp聊天机器人迅速普及。 1.为您的…...

Python从入门到精通9天(异常的处理)

异常处理 异常处理语句多个异常raise关键字常见的异常类型自定义异常 异常处理语句 在了解异常处理之前&#xff0c;我们先对它的语句进行说明&#xff0c;如下&#xff1a; try&#xff1a;表示测试代码块以查找错误 except&#xff1a;表示处理错误 finally&#xff1a;表…...

微服务学习——Docker

初识Docker 项目部署的问题 大型项目组件较多&#xff0c;运行环境也较为复杂&#xff0c;部署时会碰到一些问题: 依赖关系复杂&#xff0c;容易出现兼容性问题开发、测试、生产环境有差异 Docker Docker如何解决依赖的兼容问题的? 将应用的Libs(函数库)、 Deps&#xf…...

QMS-云质说质量 - 5 解决中小企业质量问题的钥匙在哪里?

云质QMS原创 转载请注明来源 作者&#xff1a;王洪石 引言 一个小小的质量问题可能引发蝴蝶效应 日常生活中&#xff0c;我们每天都会遇到各种各样的问题&#xff0c;并随着它们喜怒哀乐。企业也不例外&#xff0c;即使有很好的管理体系以及非常高素质的员工&#xff0c;一些错…...

基于Java+Spring+vue+element社区疫情服务平台设计和实现

基于JavaSpringvueelement社区疫情服务平台设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 …...

实战项目:手把手带你实现一个高并发内存池

项目介绍 1.这个项目做的是什么&#xff1f; 当前项目是实现一个高并发的内存池&#xff0c;他的原型是google的一个开源项目tcmalloc&#xff0c;tcmalloc全称Thread-Caching Malloc&#xff0c;即线程缓存的malloc&#xff0c;实现了高效的多线程内存管理&#xff0c;用于替…...

原理这就是索引下推呀

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 索引下推是之前面试的时候遇到的一个面试题&#xff0c;当时没有答上来&#xff0c;今天来学习一下。 介绍索引下推之前先看一下MySQL基…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...

工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配

AI3D视觉的工业赋能者 迁移科技成立于2017年&#xff0c;作为行业领先的3D工业相机及视觉系统供应商&#xff0c;累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成&#xff0c;通过稳定、易用、高回报的AI3D视觉系统&#xff0c;为汽车、新能源、金属制造等行…...

自然语言处理——循环神经网络

自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元&#xff08;GRU&#xff09;长短期记忆神经网络&#xff08;LSTM&#xff09…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...