JDK-Timer使用
简单示例
/**
* 最简单用法
*/
@Test
public void timer1() {
//创建timer,
Timer timer = new Timer();
//创建任务,类似与Runnable接口
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println(LocalDateTime.now());
}
};
//注入任务,参数配置:延迟时间,间隔时间
timer.schedule(task, 0, 1000);
while (true) {
}
}
构造函数
Timer timer = new Timer(); //Timer是一个调度器
Timer timer = new Timer(String threadName);//threadName指定执行线程名称
Timer timer = new Timer(String threadName, boolean isDaemon); //threadName指定执行线程名称,isDaemon是否守护线程,设置为守护进程,将随进程的结束自动结束。
-
修改线程名称
/** * 修改执行线程名称 */ @Test public void timer2() { //创建timer, Timer timer = new Timer("简单定时器"); //创建任务,类似与Runnable接口 TimerTask task = new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName()); System.out.println(LocalDateTime.now()); } }; //注入任务,参数配置:延迟时间,间隔时间 timer.schedule(task, 0, 500); // timer.scheduleAtFixedRate(task, 0, 500) //固定频率执行 while (true) { } } //输出: //简单定时器 //2022-04-27T14:19:32.204 //简单定时器 //2022-04-27T14:19:32.582
守护进程示例:
public static void main(String[] args) {
Timer timer = new Timer("简单定时器",true); //第二参数,true表示为守护进程,false表示不是,也是默认值
//创建任务,类似与Runnable接口
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(LocalDateTime.now());
}
};
//注入任务,参数配置:延迟时间,间隔时间
timer.scheduleAtFixedRate(task, 0, 500)
System.out.println("程序结束");
}
/**
* 如果设置为非守护进程,输出结果如下:
* 程序结束
* 简单定时器
* 2022-04-29T10:27:13.992
* 简单定时器
* 2022-04-29T10:27:14.318
* 。。。
*
*说明即使main进程结果,程序退出了,JVM还在跑这个定时任务。。。这样的话,就需要自己去控制线程的结束,如调用timer.cancle()
* 如果设置为守护进程,则输出结果如下:
* 程序结束
* 简单定时器
*
* Process finished with exit code 0
*/
说明
-
简单的频率不高的定时任务
-
不需要导入任何依赖
-
轻量级
-
单线程执行,可能会丢任务,如果陷入死循环,导致后续任务无法执行
JUC-ScheduledThreadPoolExecutor
简单示例
/**
* 最简单用法
* Runnable
*/
@Test
public void runnableTest(){
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println(LocalDateTime.now());
}
}, 1, TimeUnit.SECONDS);
while (true){}
}
/**
* 最简单用法
* Callable
* 获取返回值
*/
@Test
public void callableTest() throws ExecutionException, InterruptedException {
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
ScheduledFuture schedule = scheduledExecutorService.schedule(new Callable<LocalDateTime>() {
@Override
public LocalDateTime call() throws Exception {
System.out.println(LocalDateTime.now());
return LocalDateTime.now();
}
}, 1, TimeUnit.SECONDS);//延迟1秒,没秒执行一次
Object obj = schedule.get();//阻塞获取返回值
System.out.println("获取返回"+ obj);
while (true){}
}
-
schedule
方法只执行一次 -
更常用的为
scheduleAtFixedRate
与scheduleWithFixedDelay
-
scheduleAtFixedRate
每次执行时间为上一次任务开始起向后推一个时间间隔,即每次执行时间为 :initialDelay, initialDelay+period, initialDelay+2period, …; -
scheduleWithFixedDelay
* 每次执行时间为上一次任务结束起向后推一个时间间隔,即每次执行时间为:initialDelay, initialDelay+executeTime+delay, initialDelay+2*executeTime+2*delay。 -
由此可见,
scheduleAtFixedRate
是基于固定时间间隔进行任务调度,scheduleWithFixedDelay
取决于每次任务执行的时间长短,是基于不固定时间间隔进行任务调度
/**
* scheduleAtFixedRate
*/
@Test
public void scheduleAtFixedRateTest() throws ExecutionException, InterruptedException {
ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(10);
ScheduledFuture<?> future = scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(LocalDateTime.now());
}
}, 1, 1, TimeUnit.SECONDS);//延迟1秒,没秒执行一次
future.get();//没有返回,阻塞
}
Spring @Scheduled
简单示例
@Component
@EnableScheduling
public class Schedules {
@Scheduled(fixedDelay = 1000 * 2)//没2秒执行一次,基于上次任务结束时间往后推2秒
//@Scheduled(fixedRate = 1000 * 2)//固定频率,当然如果任务执行的超过2秒,会堆积起来,任务执行完,立马执行堆积最久的任务。相比上面丢任务会少一点
public void test1() throws InterruptedException {
System.out.println("执行线程1:"+ Thread.currentThread().getName());
System.out.println("定时任务执行1:"+ LocalDateTime.now());
}
@Scheduled(cron = "0/2 * * * * ?")
public void test2(){
System.out.println("执行线程2:"+ Thread.currentThread().getName());
System.out.println("定时任务执行2:"+ LocalDateTime.now());
}
}
Cron表达式
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
@EnableScheduling
public class Schedules {
@Scheduled(cron = "0/2 * * * * ?")
public void test1() throws InterruptedException {
Thread.sleep(5000);
System.out.println("执行线程1:"+ Thread.currentThread().getName());
System.out.println("定时任务执行1:"+ LocalDateTime.now());
}
@Scheduled(cron = "0/2 * * * * ?")
public void test2(){
System.out.println("执行线程2:"+ Thread.currentThread().getName());
System.out.println("定时任务执行2:"+ LocalDateTime.now());
}
}
// 输出
//执行线程2:scheduling-1
//定时任务执行2:2022-04-27T17:19:24.013
//执行线程1:scheduling-1
//定时任务执行1:2022-04-27T17:19:29.019
//执行线程2:scheduling-1
//定时任务执行2:2022-04-27T17:19:29.021
//执行线程2:scheduling-1
//定时任务执行2:2022-04-27T17:19:30.005
- 从输出可以看出,定时任务是单线程执行的,定时任务1 的延时影响了定时任务2,所以需要@Async注解配合使用
配合@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
@EnableScheduling
@EnableAsync
public class Schedules {
@Async
@Scheduled(cron = "0/2 * * * * ?")
public void test1() throws InterruptedException {
Thread.sleep(5000);
System.out.println("执行线程1:"+ Thread.currentThread().getName());
System.out.println("定时任务执行1:"+ LocalDateTime.now());
}
@Async
@Scheduled(cron = "0/2 * * * * ?")
public void test2(){
System.out.println("执行线程2:"+ Thread.currentThread().getName());
System.out.println("定时任务执行2:"+ LocalDateTime.now());
}
}
//输出
//执行线程2:task-1
//定时任务执行2:2022-04-27T17:20:40.070
//执行线程2:task-3
//定时任务执行2:2022-04-27T17:20:42.006
//执行线程2:task-5
//定时任务执行2:2022-04-27T17:20:44.005
//执行线程1:task-2
//定时任务执行1:2022-04-27T17:20:45.072
自定义线程池
package com.chai.springAnnotation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
@EnableAsync
public class ExecutorConfig {
@Bean
public Executor myAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(1000);//LinkedBlockingQueue
executor.setThreadNamePrefix("myTask-");
executor.initialize();
return executor;
}
}
@Component
@EnableScheduling
public class Schedules {
@Async("myAsync")
@Scheduled(cron = "0/2 * * * * ?")
public void test1() throws InterruptedException {
Thread.sleep(5000);
System.out.println("执行线程1:"+ Thread.currentThread().getName());
System.out.println("定时任务执行1:"+ LocalDateTime.now());
}
@Async("myAsync")
@Scheduled(cron = "0/2 * * * * ?")
public void test2(){
System.out.println("执行线程2:"+ Thread.currentThread().getName());
System.out.println("定时任务执行2:"+ LocalDateTime.now());
}
}
//输出
//执行线程2:myTask-2
//定时任务执行2:2022-04-27T17:34:56.048
//执行线程2:myTask-3
//定时任务执行2:2022-04-27T17:34:58.008
//执行线程2:myTask-6
//定时任务执行2:2022-04-27T17:35:00.007
//执行线程1:myTask-1
//定时任务执行1:2022-04-27T17:35:01.049
Quartz框架
<!-- quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.2</version>
</dependency>
简单示例
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.time.LocalDateTime;
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(this);
System.out.println("任务执行" + LocalDateTime.now());
}
}
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class QuartzTest {
public static void main(String[] args) throws SchedulerException {
// 1.创建调度器Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2.创建JobDetail实例
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).build();
// 3.创建Trigger实例
Trigger trigger = TriggerBuilder.newTrigger().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever()).build();
// 4. 绑定scheduler的jobDetail与trigger
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
}
}
//输出
//com.chai.quartz.MyJob@599a8d93
//任务执行2022-04-27T18:15:23.702
//com.chai.quartz.MyJob@6bbd20f6
//任务执行2022-04-27T18:15:24.698
//com.chai.quartz.MyJob@5dda8dcb
//任务执行2022-04-27T18:15:25.703
- 从输出来看,每次打印的
MyJob
的hash
值不一样,说明每次执行都会重新new
一个MyJob
慢任务测试
- 在MyJob增加延时
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.time.LocalDateTime;
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this);
System.out.println("任务执行" + LocalDateTime.now());
}
}
//输出
//com.chai.quartz.MyJob@1e0f4c37
//任务执行2022-04-27T18:20:49.904
//com.chai.quartz.MyJob@636eba44
//任务执行2022-04-27T18:20:50.905
//com.chai.quartz.MyJob@27bce3ea
//任务执行2022-04-27T18:20:51.907
-
从输出来看,Quartz是默认多线程执行,慢任务不会影响下次执行时间
-
但是我们大部分情况,希望任务能执行完一次再去执行一次,重复执行就废了
注解
-
@PersistJobDataAfterExecution
-
告诉Quartz在成功执行了Job实现类的execute方法后(没有发生任何异常),更新JobDetail中JobDataMap的数据,使得该JobDetail实例在下一次执行的时候,JobDataMap中是更新后的数据,而不是更新前的旧数据。
-
也就是说,你如果想跟新JobDataMap,并且让其下次执行就生效,那么增加这个注解
-
-
@DisallowConcurrentExecution
- 告诉Quartz不要并发地执行同一个JobDetail实例。
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.time.LocalDateTime;
@DisallowConcurrentExecution
public class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this);
System.out.println("任务执行" + LocalDateTime.now());
}
}
//输出
//com.chai.quartz.MyJob@14247628
//任务执行2022-04-27T18:27:45.049
//com.chai.quartz.MyJob@7ad1dc45
//任务执行2022-04-27T18:27:47.060
//com.chai.quartz.MyJob@2eb81e7d
//任务执行2022-04-27T18:27:49.065
- 从输出看,任务已经不会并发执行了
如果使用
@PersistJobDataAfterExecution
那么一定要使用@DisallowConcurrentExecution
,否则并发情况下,可能任务1用的是旧的JobDataMap,任务2用的是新的。数据不一致
Cron表达式
/**
* cron
* @throws SchedulerException
*/
@Test
public void test2() throws SchedulerException {
// 1.创建调度器Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2.创建JobDetail实例
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).build();
// 3.创建Trigger实例
Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")).build();
// 4. 绑定scheduler的jobDetail与trigger
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
while (true) {
}
}
传参
import org.quartz.*;
import java.time.LocalDateTime;
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class MyJob2 implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("任务执行" + LocalDateTime.now());
System.out.println("--------------------jobDetail参数------------------------");
System.out.println("name:"+context.getJobDetail().getKey().getName() + "---group : " + context.getJobDetail().getKey().getGroup());
System.out.println("用户:"+ context.getJobDetail().getJobDataMap().getString("user"));
System.out.println("---------------------trigger参数-----------------------");
System.out.println("name:"+context.getTrigger().getKey().getName() + "---group : " + context.getTrigger().getKey().getGroup());
System.out.println("id:"+ context.getTrigger().getJobDataMap().getInt("id"));
}
}
/**
* 传参
* @throws SchedulerException
*/
@Test
public void test3() throws SchedulerException {
// 1.创建调度器Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
// 2.创建JobDetail实例
JobDetail jobDetail = JobBuilder.newJob(MyJob2.class).withIdentity("j1","g1")
.usingJobData("user","chai").build();
// 3.创建Trigger实例
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("t1","tg1").usingJobData("id",123)
.withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")).build();
// 4. 绑定scheduler的jobDetail与trigger
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();
while (true) {
}
}
//输出
//任务执行2022-04-27T18:55:49.065
//--------------------jobDetail参数------------------------
//name:j1---group : g1
//用户:chai
//---------------------trigger参数-----------------------
//name:t1---group : tg1
//id:123