JAVA 定时任务的几种实现方式

本文介绍了JAVA实现定时任务的几种方式,包括JDK自带的Timer和ScheduledExecutorService,Quartz作业调度框架,以及Spring3.0以后自带的任务调度功能。详细阐述了各方式的使用方法,适合初学者和进阶者学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JAVA 定时任务的几种实现方式

JAVA实现定时任务的几种方式
@(JAVA)[spring|quartz|定时器]
  近期项目开发中需要动态的添加定时任务,比如在某个活动结束时,自动生成获奖名单,导出excel等,此类任务由于活动时间是动态的,不能把定时任务配置在配置文件或写死在代码中。当然也可以增加一个定时扫描的任务来实现。借此机会整理了AVA实现定时任务的几种常用方式,以下做简要介绍。
目前主要有以下几种实现方式:

  • JDK自带 :JDK自带的Timer以及JDK1.5+ 新增的ScheduledExecutorService;
  • Quartz :简单却强大的JAVA作业调度框架
  • Spring3.0以后自带的task :可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多;
    下面将一一介绍以上三种实现方式。

JAVA实现定时任务的几种方式
JDK 自带的定时器实现
Quartz 定时器实现
Spring 相关的任务调度

JDK 自带的定时器实现
Timer类
这个类允许你调度一个java.util.TimerTask任务。主要有以下几个方法:

  1. schedule(TimerTask task, long delay) 延迟 delay 毫秒 执行
    public static void main(String[] args) {
    for (int i = 0; i < 10; ++i) {
    new Timer("timer - " + i).schedule(new TimerTask() {
    @Override
    public void run() {
    println(Thread.currentThread().getName() + " run ");
    }
    }, 1000);
    }
    }
    out :
    timer - 2 run
    timer - 1 run
    timer - 0 run
    timer - 3 run
    timer - 9 run
    timer - 4 run
    timer - 8 run
    timer - 5 run
    timer - 6 run
    timer - 7 run

  2. schedule(TimerTask task, Date time) 特定時間執行
    public static void main(String[] args) {
    for (int i = 0; i < 10; ++i) {
    new Timer("timer - " + i).schedule(new TimerTask() {
    @Override
    public void run() {
    println(Thread.currentThread().getName() + " run ");
    }
    }, new Date(System.currentTimeMillis() + 2000));
    }
    }
    out:
    timer - 0 run
    timer - 7 run
    timer - 6 run
    timer - 8 run
    timer - 3 run
    timer - 5 run
    timer - 2 run
    timer - 1 run
    timer - 4 run
    timer - 9 run

  3. schedule(TimerTask task, long delay, long period) 延迟 delay 执行并每隔period 执行一次
    public static void main(String[] args) {
    for (int i = 0; i < 10; ++i) {
    new Timer("timer - " + i).schedule(new TimerTask() {
    @Override
    public void run() {
    println(Thread.currentThread().getName() + " run ");
    }
    }, 2000 , 3000);
    }
    }
    out:
    timer - 0 run
    timer - 5 run
    timer - 4 run
    timer - 8 run
    timer - 3 run
    timer - 2 run
    timer - 1 run
    timer - 7 run
    timer - 9 run
    timer - 6 run
    timer - 3 run
    timer - 7 run
    timer - 5 run
    timer - 4 run
    timer - 8 run
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    ScheduledExecutorService 接口实现类
    ScheduledExecutorService 是JAVA 1.5 后新增的定时任务接口,主要有以下几个方法。

  • ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
  • ScheduledFuture schedule(Callable callable,long delay, TimeUnit unit);
  • ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnitunit);
  • ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnitunit);
    1
    2
    3
    4
    默认实现为ScheduledThreadPoolExecutor 继承了ThreadPoolExecutor 的线程池特性,配合future特性,比Timer更强大。 具体用法可以阅读JDK文档;spring Task内部也是依靠它实现的。示例代码:

public static void main(String[] args) throws SchedulerException {
ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor)Executors.newScheduledThreadPool(10);
for (int i = 0; i < 10; ++i) {
executor.schedule(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " run ");
}
} , 2 , TimeUnit.SECONDS);
}
executor.shutdown();
}

out:
pool-1-thread-2 run
pool-1-thread-5 run
pool-1-thread-4 run
pool-1-thread-3 run
pool-1-thread-8 run
pool-1-thread-5 run
pool-1-thread-7 run
pool-1-thread-2 run
pool-1-thread-1 run
pool-1-thread-6 run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Quartz 定时器实现
Quartz是一个完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制。Quartz允许开发人员根据时间间隔来调度作业。它实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联。可以动态的添加删除定时任务,另外很好的支撑集群调度。简单地创建一个org.quarz.Job接口的Java类,Job接口包含唯一的方法:

public void execute(JobExecutionContext context) throws JobExecutionException;
1
2
在Job接口实现类里面,添加需要的逻辑到execute()方法中。配置好Job实现类并设定好调度时间表(Trigger),Quartz就会自动在设定的时间调度作业执行execute()。

整合了Quartz的应用程序可以重用不同事件的作业,还可以为一个事件组合多个作业。Quartz通过属性文件来配置JDBC事务的数据源、全局作业、触发器侦听器、插件、线程池等等。(quartz.properties)

通过maven引入依赖(这里主要介绍2.3.0) 注意:shiro-scheduler中依赖的是1.x版本 如果同时使用会冲突

    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.3.0</version>
    </dependency>

1
2
3
4
5
6
创建Job类
public class TestJob implements Job{
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
println(Thread.currentThread().getName() + " test job begin " + DateUtil.getCurrentTimeStr());
}
}
1
2
3
4
5
6
调度任务
public static void main(String[] args) throws InterruptedException, SchedulerException {

    Scheduler scheduler = new StdSchedulerFactory().getScheduler();
    // 开始
    scheduler.start();
    // job 唯一标识 test.test-1
    JobKey jobKey = new JobKey("test" , "test-1");
    JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
    Trigger trigger = TriggerBuilder.newTrigger()
            .withIdentity("test" , "test")
            // 延迟一秒执行
            .startAt(new Date(System.currentTimeMillis() + 1000))
            // 每隔一秒执行 并一直重复
    .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
            .build();
    scheduler.scheduleJob(jobDetail , trigger);

    Thread.sleep(5000);
    // 删除job
    scheduler.deleteJob(jobKey);
}

out :
DefaultQuartzScheduler_Worker-1test job begin 2017-06-03 14:30:33
DefaultQuartzScheduler_Worker-2test job begin 2017-06-03 14:30:34
DefaultQuartzScheduler_Worker-3test job begin 2017-06-03 14:30:35
DefaultQuartzScheduler_Worker-4test job begin 2017-06-03 14:30:36
DefaultQuartzScheduler_Worker-5test job begin 2017-06-03 14:30:37
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Quartz 主要包含以下几个部分

Job:是一个接口,只有一个方法void execute(JobExecutionContext

context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中;

JobDetail:Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。

Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等;

Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合——java.util.Calendar代表一个日历时间点,无特殊说明后面的Calendar即指org.quartz.Calendar)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。假设,我们安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用Calendar进行定点排除。

Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。

Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler#

getContext()获取对应的SchedulerContext实例;

ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。

关于简单使用,可以参考quartz的example,下面链接是一些入门帮助。

Quartz定时任务学习(一)简单任务
Quartz定时任务学习(二)web应用
Quartz定时任务学习(三)属性文件和jar

深入学习可以阅读官方文档和相关博客阅读
以下为推荐博客地址
quartz详解2:quartz由浅入深

Spring 相关的任务调度
Spring 3.0+ 自带的任务调度实现,主要依靠TaskScheduler接口的几个实现类实现。删除和修改任务比较麻烦。
主要用法有以下三种:
Spring配置文件实现
注解实现
代码动态添加
配置文件实现

spring-schedule.xml

<task:scheduler id=“myScheduler” pool-size=“10” />
<task:scheduled-tasks scheduler=“myScheduler”>
<task:scheduled ref=“job” method=“test” cron=“0 * * * * ?”/>
</task:scheduled-tasks>
1
2
3
4
5
6
注解实现

spring-schedule.xml
<task:scheduler id=“myScheduler” pool-size=“10” />
// 启用注解
<task:annotation-driven scheduler=“myScheduler”/>
1
2
3
4
@Component
public class Task{

   @Scheduled(cron="0/5 * *  * * ? ")   //每5秒执行一次       
   public void execute(){     
         DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");    
         System.out.println(sdf.format(DateTime.now().toDate())+"*********B任务每5秒执行一次进入测试");      
   }      

}
1
2
3
4
5
6
7
8
9
代码动态添加

spring-schedule.xml

1 2 3 4 5 6 7 8 @Component public class Test {
@Autowired
private ThreadPoolTaskScheduler myScheduler;

public void addJob(){

    myScheduler.schedule(new Runnable() {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " run ");
        }
    } , new CronTrigger("0/5 * *  * * ? ")); //每5秒执行一次
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring 结合 quartz 实现任务调度
spring 配置文件 spring-quartz.xml






com.test.TestJob 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 动态增加删除

@Component
public class Test {

@Autowired
private SchedulerFactoryBean quartzScheduler;

public void addJob() throws SchedulerException {

    Scheduler scheduler = quartzScheduler.getScheduler();
    JobKey jobKey = new JobKey("test", "test");
    if (scheduler.checkExists(jobKey)) {
        return;
    }
    JobDetail jobDetail = JobBuilder.newJob(TestJob.class).withIdentity(jobKey).build();
    Trigger trigger = TriggerBuilder.newTrigger().withIdentity("test", "test")
            .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();
    scheduler.scheduleJob(jobDetail, trigger);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
以上仅仅是对自己学习的总结,深入了解还需查找相关资料。比如动态增加,修改定时任务。以及Quartz的集群模式等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值