JobScheduleHelper是一个单列。

1-来看看类中的一些属性:

public static final long PRE_READ_MS = 5000; // pre read

private Thread scheduleThread;

private Thread ringThread;

private volatile boolean scheduleThreadToStop = false;

private volatile boolean ringThreadToStop = false;

private volatile static Map> ringData = new ConcurrentHashMap<>();

主要包含了两个线程和一个Map集合。

2-JobScheduleHelper.start()方法

项目启动的时间,就会进行start()方法的逻辑,会分别启动scheduleThread

和ringThread两个线程任务。

3-scheduleThread

scheduleThread主要作用就是从数据库中获取最近5s需要执行的任务,进行判断该

任务是否需要立刻执行、下次执行、还是放入时间轮中等待执行。

由于在xxl-job中master是没有中心节点的,所以调度可以每个节点都在执行,这里

为了方式相同节点同一时间执行相同的任务导致重复,利用了Mysql的行锁。

CREATE TABLE `xxl_job_lock` (

`lock_name` varchar(50) NOT NULL COMMENT '锁名称',

PRIMARY KEY (`lock_name`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

conn = XxlJobAdminConfig.getAdminConfig().getDataSource().getConnection();

connAutoCommit = conn.getAutoCommit();

conn.setAutoCommit(false);

preparedStatement = conn.prepareStatement(

"select * from xxl_job_lock where lock_name = 'schedule_lock' for update" );

preparedStatement.execute();

使用mysql的select ...for update 语句将快照读变成了当前读,会对所读取的记录

进行加锁。这里lock_name 为主键,所以这里的select 会对查询的记录加上行锁,

当其他节点也在执行此sql查询的时候,因为当前查询语句的事物还没有结束,其他节点

的查询就会进行阻塞,直到当前事物提交。

List scheduleList = XxlJobAdminConfig.getAdminConfig().getXxlJobInfoDao().

scheduleJobQuery(nowTime + PRE_READ_MS, preReadCount);

获取到最近5s需要执行的任务集合

会根据任务下次执行的时间来执行不同的分支,这里只看如果获取到的任务,还差几秒

中才需要执行怎么办?

if (nowTime > jobInfo.getTriggerNextTime() + PRE_READ_MS) {

//当前时间>任务下次执行时间+5,则说明任务过期了下次再执行

// fresh next

refreshNextValidTime(jobInfo, new Date());

} else if (nowTime > jobInfo.getTriggerNextTime()) {

//当前时间>任务下次执行时间,并且在5s内,说明任务应该马上执行

......

} else {

//还差几秒种任务需要执行

// 1、make ring second 首先获取当前任务应该在哪一秒被执行

// 因为都是最近5s需要执行的任务,所以这里计算出具体需要执行的秒就可以

int ringSecond = (int)((jobInfo.getTriggerNextTime()/1000)%60);

// 2、push time ring 放入map中

pushTimeRing(ringSecond, jobInfo.getId());

// 3、fresh next 计算下次需要执行的时间

refreshNextValidTime(jobInfo, newDate(jobInfo.getTriggerNextTime()));

}

4-pushTimeRing

private void pushTimeRing(int ringSecond, int jobId){

// push async ring

List ringItemData = ringData.get(ringSecond);

if (ringItemData == null) {

ringItemData = new ArrayList();

ringData.put(ringSecond, ringItemData);

}

ringItemData.add(jobId);

logger.debug(">>>>>>>>>>> xxl-job, schedule push time-ring : " + ringSecond + " = " + Arrays.asList(ringItemData) );

}

可以看到开始定义的map ringData 根据秒作为key,当前秒需要执行的jobId的集合

作为value.

意思就是最近5s获取的任务,如果需要在同一秒执行,则会拥有相同的key.

5-ringThread

ringThread做了什么呢

ringThread也是不断轮训,每次轮训会间隔1s

// next second, align second

TimeUnit.MILLISECONDS.sleep(1000System.currentTimeMillis()%1000);

每次先获取当前时刻的秒值,有了这个秒值,就可以去map中去获取任务集合了。

// second data

List ringItemData = new ArrayList<>();

int nowSecond = Calendar.getInstance().get(Calendar.SECOND);

// 避免处理耗时太长,跨过刻度,向前校验一个刻度;

for (int i = 0; i < 2; i++) {

List tmpData = ringData.remove( (nowSecond+60-i)%60 );

if (tmpData != null) {

ringItemData.addAll(tmpData);

}

}

将获取到的任务提交到线程池去执行调度。

if (ringItemData.size() > 0) {

// do trigger

for (int jobId: ringItemData) {

// do trigger

JobTriggerPoolHelper.trigger(jobId, TriggerTypeEnum.CRON, -1, null, null, null);

}

// clear

ringItemData.clear();

}

在xxl-job中定时任务的调度整体实现就是这样,仔细想来是不是感觉很有意思。

借助了数据库的行锁,获取最近5秒需要执行的任务,将任务真正执行时间的秒值作为

key放入map中,任务的id集合作为value.因为是最近5s的任务,所以不会存在需要执

行的任务在一个轮子中放不下的情况。

java 时间轮_基于时间轮的定时任务相关推荐

  1. calendar类计算时间距离_日期时间--JAVA成长之路

    Java中为处理日期和时间提供了大量的API,确实有把一件简单的事情搞复杂的嫌疑,各种类:Date Time Timestamp Calendar...,但是如果能够看到时间处理的本质就可以轻松hol ...

  2. java redis管理_优雅时间管理Java轻松做到,想学么?

    原标题:优雅时间管理Java轻松做到,想学么? 来源 |http://rrd.me/gCQHp 前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失 ...

  3. uuid表示时间的部分_基于时间UUID的妙用

    1.jar包获取 https://github.com/cowtowncoder/java-uuid-generator/ com.fasterxml.uuid java-uuid-generator ...

  4. mysql 时间国际化_日期时间处理和国际化相关

    日期/时间的国际化,不仅涉及到地理位置(Locale,比如星期.月份等日历本地化表示),还涉及到时区(TimeZone,针对UTC/GMT的偏移量).时区不仅是地理位置规定,更是政治规定,比如中国从地 ...

  5. java校园导航_基于Android平台的校园导航系统

    摘要:随着移动网络通讯技术的迅速发展和基于安卓技术的手持终端设备的日益普及,Android手机在学生群体中普遍使用,各类高校内部一般都具有良好的校园网络环境.虽然现在出现了各式各样的地图导航软件,但在 ...

  6. mysql 时间点_MySQL 基于时间点与位置恢复

    基于时间点与位置恢复 利用二进制日志可以实现基于时间与位置的恢复,例如由于误操作删除了一张表,这时候完全恢复是没用的,因为日志里面还是存在错误语句,我们需要的是恢复到误操作之前的状态,然后跳过误操作数 ...

  7. 动态时间规整_动态时间规整下时间序列子序列的搜索与挖掘

    一.DTW的背景 对于时间序列数据挖掘算法的相似性搜索来说最大的瓶颈就是所花费的时间,所以大多数关于时间序列数据挖掘的学术研究都在考虑数百万个时间序列对象时停滞不前,而许多工业和科学都在数十亿个等待探 ...

  8. vb 软件时间限制_带时间限制的软件加密锁

    "时间就是金钱"这句话完美的诠释了软件的价值.而精锐 5 时钟锁最大程度的保证软件价值并帮助软件开发商解决业务问题. ​ ​ 精锐 5 时钟锁采用硬件时钟功能,内置独立时钟芯片,带 ...

  9. mysql 格林时间转换_格林时间转换成正常时间

    uscdbmt@rac1:~> date +%s 1414741902 oracle中怎么把这个1414741902转换成正常时间格式 select Numtodsinterval(141474 ...

最新文章

  1. 利用Mono.Cecil动态修改程序集来破解商业组件(仅用于研究学习)
  2. 阻塞队列BlockingQueue用法
  3. 飞龙的程序员书单 – 思想、工程、架构、职业发展
  4. ubuntu yum安装_ubuntu 制作本地yum仓库
  5. H.264 SPS/PPS成员值含义
  6. 7-76 打印选课学生名单 (25分)
  7. 微商分销系统哪家好,要怎么做?
  8. 网络期刊(个人使用)
  9. Unity SKFramework框架(十七)、FreeCameraController 上帝视角/自由视角相机控制脚本
  10. git: Couldn‘t find remote ref
  11. 2019 小米校招笔试题 小米大礼包
  12. beforeSend 出现跨域问题,header里直接设置token就没问题----Day1
  13. nginx NSS error -12263 (SSL_ERROR_RX_RECORD_TOO_LONG) 处理
  14. R语言绘制QQ图实战(qqplot函数、qqnorm函数、qqline函数)
  15. 音频质量的评价方法:简单梳理
  16. Linux - top命令详解(监视进程和Linux整体性能)
  17. python画哪吒_哪吒票房逼近30亿,python爬取哪吒短评分析
  18. IOS之plist文件
  19. 《ANSYS Workbench有限元分析实例详解(静力学)》——2.4 ACT插件
  20. Windows服务器——网络负载平衡

热门文章

  1. 架构师成长之路(4)--高可用高并发架构(图谱)
  2. 服务器网站需要多大硬盘,做网站服务器硬盘多大
  3. Android 之Google认证GMS详细解剖
  4. 搜索引擎高级搜索指令
  5. 单片机-stm32-使用cdc类实现vcp(虚拟串口)
  6. Python综合基础及机器学习面试题
  7. 谷歌小恐龙-有网也能玩儿
  8. 2022最新微信小程序常见面试题总结
  9. linux 发送at指令,AT指令(打电话与发短信)
  10. angular中copy和extend用法实例