java实现周期性执行(定时任务)

 更新时间:2022年09月02日 16:22:40   作者:你是我的小丫小太阳  
这篇文章主要为大家详细介绍了java实现周期性执行定时任务,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

最近是遇到一个设备在线离线的判定问题,设计是每个多长时间(常规的定时任务)检测一次设备是否在前,当检测到里离线时,我们不能立马判断为离线,而是要在重试多测几次,只要一次成功就返回判定为在线,多次都不成功侧是离线,我这里相当了用ScheduledThreadPoolExecutor来实现,如有不足还请提出。如下:

ScheduledThreadPoolExecutor的介绍:

ScheduledThreadPoolExecutor,它可另行安排在给定的延迟后运行命令,或者定期执行命令。需要多个辅助线程时,或者要求 ThreadPoolExecutor 具有额外的灵活性或功能时,此类要优于Timer。

ScheduledThreadPoolExecutor的使用详解

当程序需要用到一个定时器处理问题的时候,并且需要处理的频率是很快的,这就需要一个稳定的定时器来保证数据的长久进行。ScheduledThreadPoolExecutor这个类就是个很好的选择。正常情况下,定时器我们都是用Timer和TimerTask这两个类就能完成定时任务,并且设置延长时间和循环时间间隔。
ScheduledThreadPoolExecutor也能完成Timer一样的定时任务,并且时间间隔更加准确。

误差说明:

我在后台程序看看一下Timer执行程序是有可能延迟1、2毫秒,如果是1秒执行一次的任务,1分钟有可能延迟60毫秒,一小时延迟3600毫秒,相当于3秒,实际用户看不出什么区别。 但是,如果我的程序需要每40毫秒就执行一次任务,如果还是有1、2毫秒的误差,1秒钟就有25毫秒的误差,大概40秒就有1秒的误差,十几分钟就有十几秒的误差,这对UI显示来说是延迟非常严重的了。 而我用ScheduledThreadPoolExecutor来做40毫秒的间隔任务,一般十几分钟才有1秒多的误差,这个还是能接受的。 这也是我用ScheduledThreadPoolExecutor这个类的原因。

使用Timer和TimerTask存在一些缺陷:

1.Timer只创建了一个线程。当你的任务执行的时间超过设置的延时时间将会产生一些问题。
2.Timer创建的线程没有处理异常,因此一旦抛出非受检异常,该线程会立即终止。
JDK 5.0以后推荐使用ScheduledThreadPoolExecutor。该类属于Executor Framework,它除了能处理异常外,还可以创建多个线程解决上面的问题

Timer和TimerTask的使用 :

这里就不做过多的描述了,重点在ScheduledThreadPoolExecutor。

Timer timer = new Timer();
      timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.e("time:");
 
            }
        }, 2000, 40);
//2000表示第一次执行任务延迟时间,40表示以后每隔多长时间执行一次run里面的任务

ScheduledThreadPoolExecutor的使用:

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
 
/**
 * 设备在线延时检查检查
 */
@Slf4j
public class DelayedCheckDeviceSchedule {
 
  public static final Integer CONNECT_TIME_OUT = 10000;
 
//引用的业务层
  private ITblBaseService baseService = SpringContextHolder.getBean(ITblBaseService .class);
 
  private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = null;
 
  /**
   * 需要延时检查的设备状态的设备id集合
   */
  public static Set<String> deviceSet = new HashSet<>();
 
  /**
   * 当前执行点
   */
  private AtomicInteger currentAtomicInteger = new AtomicInteger(1);
 
 
  /**
   * 初始化任务
   * @param delay 延迟几秒执行
   * @param checkCount 需要检测的次数
   * @param deviceId 设备Id
   * @param deviceType 设备类型
   * @return
   */
  public boolean init(long delay, int checkCount, String deviceId, String deviceType) {
    log.info("第一次初始化时间"+deviceId+":"+System.currentTimeMillis());
    if (deviceSet.add(deviceId) && deviceConnectModel!=null) {
      this.scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(4);
      this.scheduledThreadPoolExecutor.schedule(new Runnable() {
        @Override
        public void run() {
          executor(delay, checkCount, deviceId, deviceType);
        }
      }, delay, TimeUnit.SECONDS);
      return true;
    }
    return false;
  }
 
  /**
   * 执行体
   */
  private void executor(long delay, int checkCount, String deviceId, String deviceType) {
    log.info("第"+currentAtomicInteger.get()+"执行时间"+deviceId+":"+System.currentTimeMillis());
    if (deviceSet.contains(deviceId) && currentAtomicInteger.get() < (checkCount+1)) {
      //执行逻辑
          
       //当满足条件时,停止任务
        if(currentAtomicInteger.get()==checkCount){ 
          //需要处理的逻辑
 
          //停止任务
          this.scheduledThreadPoolExecutor.shutdownNow();
          deviceSet.remove(deviceId);
        }else {
          //下一次执行
          currentAtomicInteger.getAndIncrement();
          this.scheduledThreadPoolExecutor.schedule(new Runnable() {
            @Override
            public void run() {
              executor(delay, checkCount, deviceId, deviceType);
            }
          }, delay, TimeUnit.SECONDS);
     
       }
    } else {
      this.scheduledThreadPoolExecutor.shutdownNow();
      this.scheduledThreadPoolExecutor = null;
    }
  }
 
  /**
   * 停止检测任务
   * @param deviceId
   * @return
   */
  public static boolean stop(String deviceId) {
    return deviceSet.remove(deviceId);
  }
 
}

在需要周期性检查的时候引入:

DelayedCheckDeviceSchedule delayedCheckDeviceSchedule = new DelayedCheckDeviceSchedule();
delayedCheckDeviceSchedule.init(10, 3, panModel.getDeviceId(), "pan");

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • MyBatis一次执行多条SQL语句的操作

    MyBatis一次执行多条SQL语句的操作

    这篇文章主要介绍了MyBatis一次执行多条SQL语句的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • java/word+fusionchart生成图表深入分析

    java/word+fusionchart生成图表深入分析

    本文将以java/word+fusionchart生成图表问题深入解析,有兴趣的朋友可以参考下
    2012-11-11
  • Java的web开发中SSH框架的协作处理应用笔记

    Java的web开发中SSH框架的协作处理应用笔记

    这篇文章主要介绍了Java的web开发中SSH框架的协作处理应用笔记,SSH是指Struts和Spring以及Hibernate的框架搭配,需要的朋友可以参考下
    2015-12-12
  • Java中枚举的实现与应用详解

    Java中枚举的实现与应用详解

    这篇文章主要介绍了Java中枚举的实现与应用详解,EnumTest中还有一个VALUES数组,里面存储着所有的枚举实例,调用values方法时返回VALUES数组的clone,需要的朋友可以参考下
    2023-12-12
  • Java中StringTokenizer的用法简介汇总

    Java中StringTokenizer的用法简介汇总

    StringTokenizer 是出于兼容性的原因而被保留的遗留类(虽然在新代码中并不鼓励使用它),建议所有寻求此功能的人使用 String 的 split 方法或 java.util.regex 包,本文给大家整理了Java StringTokenizer用法,感兴趣的朋友一起看看吧
    2022-07-07
  • 快速解决SpringMVC @RequestBody 用map接收请求参数的问题

    快速解决SpringMVC @RequestBody 用map接收请求参数的问题

    今天小编就为大家分享快速解决SpringMVC @RequestBody 用map接收请求参数的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • Java自定义实现equals()方法过程解析

    Java自定义实现equals()方法过程解析

    这篇文章主要介绍了Java自定义实现equals()方法过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-02-02
  • Java超详细教你写一个斗地主洗牌发牌系统

    Java超详细教你写一个斗地主洗牌发牌系统

    这篇文章主要介绍了怎么用Java来你写一个斗地主种洗牌和发牌的功能,斗地主相信大家都知道,同时也知道每一局都要洗牌打乱顺序再发牌,本篇我们就来实现这个功能,感兴趣的朋友跟随文章往下看看吧
    2022-03-03
  • SpringBoot Test的webEnvironment源码解读

    SpringBoot Test的webEnvironment源码解读

    这篇文章主要为大家介绍了SpringBoot Test的webEnvironment源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • java.lang.Runtime.exec的左膀右臂:流输入和流读取详解

    java.lang.Runtime.exec的左膀右臂:流输入和流读取详解

    这篇文章主要介绍了java.lang.Runtime.exec的左膀右臂:流输入和流读取详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11

最新评论