Java实现定时任务

2023-11-02

1 使用java.util.Timer

这种方式的定时任务主要用到两个类,Timer 和 TimerTask,使用起来比较简单。其中 Timer 负责设定 TimerTask 的起始与间隔执行时间。 TimerTask是一个抽象类,new的时候实现自己的 run 方法,然后将其丢给 Timer 去执行即可。
代码示例:

import java.time.LocalDateTime;
import java.util.Timer;
import java.util.TimerTask;

public class Schedule {
    public static void main(String[] args) {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
            }
        };
        // 在指定延迟0毫秒后开始,随后地执行以2000毫秒间隔执行timerTask 
        new Timer().schedule(timerTask, 0L, 2000L);
        System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
    }
}

在这里插入图片描述
缺点:

  • Timer 的背后只有一个线程,不管有多少个任务,都只有一个工作线程串行执行,效率低下
  • 受限于单线程,如果第一个任务逻辑上死循环了,后续的任务一个都得不到执行
  • 依然是由于单线程,任一任务抛出异常后,整个 Timer 就会结束,后续任务全部都无法执行

2 使用ScheduledExecutorService

ScheduledExecutorService 即是 Timer 的替代者,JDK 1.5 并发包引入,是基于线程池设计的定时任务类。每个调度任务都会分配到线程池中的某一个线程去执行,任务就是并发调度执行的,任务之间互不影响

Java 5.0引入了java.util.concurrent包,其中的并发实用程序之一是ScheduledThreadPoolExecutor ,它是一个线程池,用于以给定的速率或延迟重复执行任务。它实际上是Timer/TimerTask组合的更通用替代品,因为它允许多个服务线程,接受各种时间单位,并且不需要子类TimerTask (只需实现Runnable)。使用一个线程配置ScheduledThreadPoolExecutor使其等效于Timer 。

代码示例:

import java.time.LocalDateTime;
import java.util.concurrent.*;

public class Schedule {
    public static void main(String[] args) {
        // 创建一个ScheduledThreadPoolExecutor线程池,核心线程数为5
        ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5);
        // 创建Runnable打印当前线程和当前时间
        Runnable r = () -> System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
        /**
         * schedule:只执行一次调度
         * scheduleAtFixedRate:一开始就计算间隔时间,如果任务超过间隔时间,那么就直接开始下一个任务
         * scheduleWithFixedDelay:任务无论执行多久,都要等待上一轮任务完成之后再间隔指定时间,然后才开始下一个任务
         */
         // 在指定1秒延迟后执行r,之后每两秒执行一次
        scheduledExecutorService.scheduleAtFixedRate(r, 1, 2, TimeUnit.SECONDS);
    }
}

在这里插入图片描述

3 使用Spring Task

Spring Task 底层是基于 JDK 的 ScheduledThreadPoolExecutor 线程池来实现的。直接通过Spring 提供的 @Scheduled 注解即可定义定时任务,非常方便。

以Spring Boot来作为示例,步骤为

  1. 在启动类所在包下创建Schedule 类(在没有配置@ComponentScan的情况下,Spring Boot只会默认扫描启动类所在包的spring组件)
  2. 在该类上添加@Component和@EnableScheduling注解
  3. 在方法上添加@Scheduled注解,该注解主要参数如下
String cron() default "";  // 支持cron表达式

long fixedDelay() default -1;  // 在最后一次调用结束和下一次调用开始之间的时间间隔,以毫秒为单位
String fixedDelayString() default "";  // 同上,类似ScheduledExecutorService的scheduleWithFixedDelay

long fixedRate() default -1;  // 在调用之前的时间间隔,以毫秒为单位
String fixedRateString() default "";  // 同上,类似ScheduledExecutorService的scheduleAtFixedRate

long initialDelay() default -1;  // 在第一次执行fixedRate()或fixedDelay()任务之前要延迟的毫秒数
String initialDelayString() default "";  // 同上

代码示例:

import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@EnableScheduling
public class Schedule {
    @Scheduled(fixedRate = 2000L)
    public void task() {
        System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
    }
}

在这里插入图片描述

  • 优点: 简单,轻量,支持 Cron 表达式
  • 缺点 :默认只支持单机,是单线程的,并且提供的功能比较单一

可以通过@EnableAsync和 @Async开启多线程

import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component
@EnableAsync  // 开启异步多线程
@EnableScheduling
public class Schedule {

    @Async
    @Scheduled(fixedRate = 2000L)
    public void task() {
        System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
    }
}

在这里插入图片描述

使用@EnableAsync注解后,默认情况下,Spring将搜索关联的线程池定义:上下文中的唯一org.springframework.core.task.TaskExecutor
的bean,或者名为“taskExecutor”的java.util.concurrent.Executor
的bean。如果两者都无法解析,则将使用org.springframework.core.task.SimpleAsyncTaskExecutor来处理异步方法调用。

在这里插入图片描述

TaskExecutor实现为每个任务启动一个新线程,异步执行它。 支持通过“concurrencyLimit”bean 属性限制并发线程。默认情况下,并发线程数是无限的,所以使用默认的线程池有导致内存溢出的风险

注意:刚才的运行结果看起来是线程复用的,而实际上此实现不重用线程应尽量实现一个线程池TaskExecutor ,特别是用于执行大量短期任务。不要使用默认的SimpleAsyncTaskExecutor。

import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.concurrent.Executor;

@Component
@EnableAsync
@EnableScheduling
public class Schedule {

    @Async
    @Scheduled(fixedRate = 2000L)
    public void task() {
        System.out.println("当前线程:" + Thread.currentThread().getName() + " 当前时间" + LocalDateTime.now());
    }


    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(10);
        taskExecutor.setMaxPoolSize(50);
        taskExecutor.setQueueCapacity(200);
        taskExecutor.setKeepAliveSeconds(60);
        taskExecutor.setThreadNamePrefix("自定义-");
        taskExecutor.setAwaitTerminationSeconds(60);
        return taskExecutor;
    }
}

在这里插入图片描述

上面提到的一些定时任务的解决方案都是在单机下执行的,适用于比较简单的定时任务场景比如每天凌晨备份一次数据。如果我们需要一些高级特性比如支持任务在分布式场景下的分片和高可用的话,我们就需要用到分布式任务调度框架了,比如Quartz、Elastic-Job、XXL-JOB、PowerJob,本文就不再详细进行介绍了,感兴趣的可以自行查阅相关资料。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java实现定时任务 的相关文章

随机推荐

  • c语言动态内存管理

    在C语言中 有几个动态内存管理的函数 分别是malloc calloc realloc和free free free函数用于释放由malloc calloc或realloc函数分配的内存空间 它接受一个指向要释放的内存的指针作为参数 注意
  • 在kali linux里利用SQLmap实现SQL注入

    SQLMap简介 SQLMap 是一个自动化的SQL注入工具 其主要功能是扫描 发现并利用给定URL的SQL注入漏洞 内置了很多绕过插件 支持的数据库是MySQL Oracle PostgreSQL Microsoft SQL Server
  • 第五阶段学习测试

    逐梦 一 单选题 1 下列各项中 执行流程正确的是 A InputFormat Mapper Reducer OutputFormat B Mapper InputFormat Reducer OutputFormat C InputFor
  • 《动手学深度学习 Pytorch版》 6.6 卷积神经网络

    import torch from torch import nn from d2l import torch as d2l 6 6 1 LeNet LetNet 5 由两个部分组成 卷积编码器 由两个卷积核组成 全连接层稠密块 由三个全连
  • Docker存储卷(Volume)

    简介 想要了解Docker Volume 首先我们需要知道Docker的文件系统是如何工作的 Docker镜像是由多个文件系统 只读层 叠加而成 当我们启动一个容器的时候 Docker会加载只读镜像层并在其上镜像栈顶部添加一个读写层 如果运
  • RTP如何打包H264数据

    拿到H264的裸流数据是 一般码流结构是SPS PPS I帧 P帧 SPS PPS I帧 P帧 用RTP打包H264数据时 SPS和PPS可以不发 直接发I帧和P帧数据即可 还要看I帧和P帧有多大 如果小于MTU就直接加RTP包发送就可以
  • 关于Unity任何版本点击Play运行就黑屏,除了摄像机窗口其他全部黑掉的问题解决~

    问题如图所示 这个问题一般人很少遇到 我却遇见了N次 每次重装都是这样从5 0到5 4 0版本都是如此 几乎崩溃 咨询很多人都一脸茫然 重装系统 重装软件 更新版本 都不行 后来 偶然间 我这样解决了问题 1 打开Unity 点击Edit
  • 【从零开始学c++】————模板初阶

    模板初阶 一 模板函数 1 模板函数的概念 2 模板的匹配原则 二 类模板 1 类模板的定义格式 2 类模板的实例化 一 模板函数 如何实现一个通用的的两个数相加的函数呢 我们可以通过函数重载把各个类型的两个数相加给写出来 如下 int A
  • python+requests+pytest 接口自动化框架(八)

    今日内容 接口自动化测试框架封装之数据类型处理以及DDT数据驱动封装 一 数据类型处理 read extract data tag id 替换成 110 json tag id read extract data tag id 二 DDT数
  • 解决 error: style attribute '@android:attr/windowEnterAnimation' not found.

    在Project gradle properties中添加 android enableAapt2 false
  • 5.3.4 因特网的路由协议(四)BGP协议

    5 3 4 因特网的路由协议 四 BGP协议 我们学习的RIP协议 5 3 2 因特网的路由协议 二 基于距离向量算法的RIP协议 和OSPF协议 5 3 3 因特网的路由协议 三 OSPF协议 都是内部网关协议 都是只能作用于一个自治系统
  • 【原创】xenomai UDD介绍与UDD用户态驱动示例

    xenomai UDD与用户态驱动示例 本文介绍xenomai UDD原理和相关代码 并给出一个基于UDD的用户态操作GPIO的示例 以及内核收发网络包与用户态操作网卡收发包的CPU耗时对比 版权声明 本文为本文为博主原创文章 转载请注明出
  • Web.xml配置详解

    1 定义头和根元素 部署描述符文件就像所有XML文件一样 必须以一个XML头开始 这个头声明可以使用的XML版本并给出文件的字符编码 DOCYTPE声明必须立即出现在此头之后 这个声明告诉服务器适用的servlet规范的版本 如2 2或2
  • 浏览器中地址框输入url地址会进行什么操作?

    一 http请求流程 http 超文本传输协议 是基于TCP协议之上的应用层协议 http的请求会进行以下操作 一 http请求流程 1 1 url域名解析成ip地址 dns解析 1 2 建立三次握手TCP连接 1 2 1 报文中SYN S
  • hashmap转红黑树的两个条件

    一个是链表长度到8 实际是要超过8 后面有补充说明 一个是数组长度到64 上图所示是判断链表长度到达8调用treeifyBin方法转换红黑树 TREEIFY THRESHOLD的值为8 TREEIFY THRESHOLD 1 7 所以bin
  • Pandas中对DataFrame列名进行重命名

    目录 1 重命名Pandas DataFrame Column 列 2 在创建Dataframe时指定列名 3 使用 list 设置全部列名 Pandas是一个用于数据分析和操作的Python库 在pandas中几乎所有的操作都围绕着Dat
  • 5款超级好用的命令行工具,提升你的开发效率!

    微信公众号公众号平凡而诗意 更多精彩内容第一时间推送 如果你是一个喜欢使用Linux系统 或者经常和Linux打交道的开发人员 那么 终端和命令行一定是接触最为频繁的工具之一 Linux不像Windows那样 有丰富的桌面应用 但是 却有很
  • JavaScript基础背诵点

    1 JavaScript是一种基于对象和事件驱动具有安全性能的脚本语言 HTML语言是网页设计中普遍采用的一种超文本标记语言 但HTML自身并不能为网页提供动态支持 也不能接受用户输入 更不能对用户请求做出反应 JavaScript可以嵌入
  • linux的crontab定时配置全过程

    今天因为业务需求 需要在服务器上配置定时任务执行数据库的sql语句 所以使用到了linux 的crontab 特此记录 分享一下自己的过程 首先呢 我们知道crontab是在linux中用于设定在某些时间可以自动执行某些命令的功能 在lin
  • Java实现定时任务

    文章目录 1 使用java util Timer 2 使用ScheduledExecutorService 3 使用Spring Task 1 使用java util Timer 这种方式的定时任务主要用到两个类 Timer 和 Timer