Java项目实现定时任务的三种方法

目录
  • 1 使用java.util.Timer
  • 2 使用ScheduledExecutorService
  • 3 使用Spring Task
  • 总结

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,本文就不再详细进行介绍了,感兴趣的可以自行查阅相关资料。

总结

到此这篇关于Java项目实现定时任务的三种方法的文章就介绍到这了,更多相关Java定时任务实现内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java定时任务cron表达式每周执行一次的坑及解决

    目录 java定时任务cron表达式每周执行一次的坑 cron表达式指定每周几调度 java定时任务cron表达式每周执行一次的坑 java springboot 利用schedule执行定时任务是很常用的功能,有一个很常用的网站就是在线Cron表达式生成器,但是在这个网站最近遇到一个坑. 我要每周四执行一次,我把我写的表达式在这个网站验证了,是可以的,你看最近10次执行时间,同时看一下我右下角的日历,没问题吧,实际上坑已经出现了. 在这个验证前,我先在网上查了一次资料,1周日,2是周一,那就周

  • Java使用quartz实现定时任务示例详解

    目录 正文 配置文件 pom 定时任务和触发器 定时任务的业务代码 正文 最近新到了一个项目,用到定时任务的地方是真滴多. 就稍微研究了一下,来做个demo. 其实定时任务使用很广泛也很方便,之前做的人事管理项目,就会定期执行定时任务计算工资,对于一个saas服务来说,即时的计算所有员工的工资有点奢侈,所以在每周末计算一次就ok了. 国外有的公司是一周发一次工资,所以当时的逻辑是一周算一次.在国内就一月一次很ok了.在当时的report服务中,也是定时任务同步数据到Birt服务,然后展现数据.

  • Java使用线程池执行定时任务

    目录 1.schedule 2.scheduleAtFixedRate 3.scheduleWithFixedDelay 总结 前言: 在 Java 语言中,有两个线程池可以执行定时任务:ScheduledThreadPool 和 SingleThreadScheduledExecutor,其中 SingleThreadScheduledExecutor 可以看做是 ScheduledThreadPool 的单线程版本,它的用法和 ScheduledThreadPool 是一样的,所以本文重点来

  • java开发主流定时任务解决方案全横评详解

    目录 引言 Crontab 目标定位 使用方式 实现原理 方案分析 Spring Task 目标定位 使用方式 实现原理 方案分析 ElasticJob 目标定位 使用方式 实现原理 方案分析 XXLJob 目标定位 使用方式 实现原理 方案分析 Serverless Job 目标定位 使用方式 实现原理 方案分析 总结 引言 定时任务作为一种按照约定时间执行预期逻辑的通用模式,在企业级开发中承载着丰富的业务场景,诸如后台定时同步数据生成报表,定时清理磁盘日志文件,定时扫描超时订单进行补偿回调等

  • 一文搞懂如何实现Java,Spring动态启停定时任务

    目录 为什么需要定时任务 Java定时任务的原理 Timer+TimerTask ScheduledThreadPoolExecutor Timer VS ScheduledThreadPoolExecutor Spring定时任务 @Scheduled定时任务原理(源码) 为什么需要定时任务 定时任务的应用场景十分广泛,如定时清理文件.定时生成报表.定时数据同步备份等. Java定时任务的原理 jdk自带的库中,有两种技术可以实现定时任务,一种是Timer,另一种是ScheduledThrea

  • Java实现定时任务最简单的3种方法

    目录 一.Timer 二.ScheduledExecutorService 三.Spring Task 1.开启定时任务 2.添加定时任务 Cron 表达式 知识扩展:分布式定时任务 1.ZSet 实现方式 2.键空间通知 总结 一.Timer Timer是JAVA自带的定时任务类,实现如下: public class MyTimerTask { public static void main(String[] args) { // 定义一个任务 TimerTask timerTask = ne

  • Java项目实现定时任务的三种方法

    目录 1 使用java.util.Timer 2 使用ScheduledExecutorService 3 使用Spring Task 总结 1 使用java.util.Timer 这种方式的定时任务主要用到两个类,Timer 和 TimerTask,使用起来比较简单.其中 Timer 负责设定 TimerTask 的起始与间隔执行时间. TimerTask是一个抽象类,new的时候实现自己的 run 方法,然后将其丢给 Timer 去执行即可. 代码示例: import java.time.L

  • Java 实现定时任务的三种方法

    是的,不用任何框架,用我们朴素的 Java 编程语言就能实现定时任务. 今天,栈长就介绍 3 种实现方法,教你如何使用 JDK 实现定时任务! 1. sleep 这也是我们最常用的 sleep 休眠大法,不只是当作休眠用,我们还可以利用它很轻松的能实现一个简单的定时任务. 实现逻辑: 新开一个线程,添加一个 for/ while 死循环,然后在死循环里面添加一个 sleep 休眠逻辑,让程序每隔 N 秒休眠再执行一次,这样就达到了一个简单定时任务的效果. 实现代码如下: private stat

  • Java追加文件内容的三种方法实例代码

    整理文档,搜刮出一个Java追加文件内容的三种方法的代码,稍微整理精简一下做下分享. import Java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.RandomAccessFile;

  • java 获取当前时间的三种方法

    总结java里面关于获取当前时间的一些方法 System.currentTimeMillis() 获取标准时间可以通过System.currentTimeMillis()方法获取,此方法不受时区影响,得到的结果是时间戳格式的.例如: 1543105352845 我们可以将时间戳转化成我们易于理解的格式 SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss z"); Date date = n

  • vue项目刷新当前页面的三种方法

    想必大家在刨坑vue的时候也遇到过下面情形:比如在删除或者增加一条记录的时候希望当前页面可以重新刷新或者如下面这种: 如果希望点击确定的时候,Dialog 对话框关闭的时候,当前http://localhost:9530/#/supplier/supplierAll页面可以重新刷新下 那么表格的数据可以重新加载,Dialog 对话框设置的数据可以在确定后刷新出现在页面上 这时候我们最直接的思维就是想到下面这种: 但是,试过的会发现用vue-router重新路由到当前页面,页面是不进行刷新的,根本

  • java调用Restful接口的三种方法

    目录 1,基本介绍 2,HttpURLConnection实现 3.HttpClient实现 4.Spring的RestTemplate 1,基本介绍 Restful接口的调用,前端一般使用ajax调用,后端可以使用的方法比较多, 本次介绍三种: 1.HttpURLConnection实现 2.HttpClient实现 3.Spring的RestTemplate 2,HttpURLConnection实现 @Controller public class RestfulAction { @Aut

  • Django项目连接MongoDB的三种方法

    目录 Django 和 MongoDB 设置 安装: 创建: 激活: 使用 PyMongo 连接 Django 和 MongoDB 使用 MongoEngine 连接 Django 和 MongoDB 使用 Djongo 连接 Django 和 MongoDB Django 和 MongoDB 教程 有三种方法连接Django到MongoDB数据库 1.PyMongo:PyMongo 是 MongoDB 与 Django 交互的标准驱动程序.这是在 Python 中使用 MongoDB 的官方和

  • java字符转码的三种方法总结及实例

    java字符转码:三种方法 转码成功的前提:解码后无乱码 转码流程:文件(gbk)-->解码-->编码--->文件(utf-8) 注:如有问题请留言 下面具体的实例  方法一:Java.lang.String //用于解码的构造器: String(byte[] bytes, int offset, int length, String charsetName) String(byte[] bytes, String charsetName) 用于编码的方法: byte[] getByte

  • Java复制文件常用的三种方法

    复制文件的三种方法: 1.Files.copy(path, new FileOutputStream(dest));. 2.利用字节流. 3.利用字符流. 代码实现如下: package com.tiger.io; import java.io.*; import java.nio.file.*; /** * 复制文件的三种方式 * @author tiger * @Date */ public class CopyFile { public static void main(String[]

  • java读取文件内容的三种方法代码片断分享(java文件操作)

    复制代码 代码如下: try {           // 方法一           BufferedReader br = new BufferedReader(new FileReader(new File(                   "D:\\1.xls")));           // StringBuilder bd = new StringBuilder();           StringBuffer bd = new StringBuffer();   

随机推荐