大家好,又见面了,我是你们的朋友全栈君。
个人学习笔记分享,当前能力有限,请勿贬低,菜鸟互学,大佬绕道 如有勘误,欢迎指出和讨论,本文后期也会进行修正和补充
定时器顾名思义,即定时触发某个事件,分离开来,即包含三个因素:定时,触发,某个事件,本文也将以此为基础介绍五种常见的定时器
本文只做基于SpringBoot的示例,其余版本的请自行查阅资料,大同小异
定时器的目的即为了在某个时间点,程序自身主动触发某个事件,而不需要外力去开启或者启动,以节省人力并统一管理
是不是觉得很常见?
Timer
spring
的quartz
框架本文仅简述前3种,比较简单易懂,quartz会专门分离出来整理
使用注解标记需要定时执行的方法,并设置执行时间,便可使其在指定的时间执行指定方法
@Scheduled
标记目标方法,参数为执行时间@EnableScheduling
标记目标方法所在的类,或者直接标记项目启动类@Scheduled
为方法注解,用于标记某个方法在何时定时执行@EnableScheduling
进行使用,该注解用于标记某个类,开启定时任务,通常标记在定时器所在的类,或者直接设置在项目启动类上@Scheduled(fixedDelay = 5000)
:方法执行完成后等待5秒再次执行
@Scheduled(fixedRate = 5000)
:方法每隔5秒执行一次
@Scheduled(initialDelay=1000, fixedRate=5000)
:延迟1秒后执行第一次,之后每隔5秒执行一次
fixedDelayString
、fixedRateString
、initialDelayString
:与上诉三种作用一直,但参数为字符串类型,因而可以使用占位符,形如
@Scheduled(fixedDelayString = "${
time.fixedDelay}")
@Scheduled(cron = "0 0,30 0,8 ? * ? ")
:方法在每天的8点30分0秒执行,参数为字符串类型,那么同理也可使用占位符,cron表达式请另行查阅资料,推荐看这篇文章:https://www.jianshu.com/p/1defb0f22ed1
示例1:每隔3秒执行一次
@Component
@EnableScheduling
public class ScheduleTest {
private int count = 0;
/** * 每3秒钟执行一次 */
@Scheduled(cron = "*/3 * * * * ?")
public void test1() {
System.out.println(count + ":" + (new Date()).toString());
count++;
}
}
示例2:第一次等待10秒,之后每3秒一次
@Component
@EnableScheduling
public class ScheduleTest {
private int count = 0;
/** * 第一次等待10秒,之后每3秒钟执行一次 */
@Scheduled(initialDelay = 10000, fixedRate = 3000)
public void test1() {
System.out.println(count + ":" + (new Date()).toString());
count++;
}
}
使用非常简单,这里先给出样例,在对照进行介绍
代码如下
package com.yezi_tool.demo_basic.test;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
@Component
public class TimerTest {
private Integer count = 0;
public TimerTest() {
testTimer();
}
public void testTimer() {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
try {
//do Something
System.out.println(new Date().toString() + ": " + count);
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
}, 0, 1000);
}
}
执行结果
可以看到每隔1s打印一次count并自增1
核心包括Timer和TimerTask,均为jkd自带的工具类,代码量分别为721行和162行(包括注释),都不多,有兴趣的可以直接看看源码
TimerTask实际上就是一个Runnable而已,继承Runnable并添加了几个自定义的参数和方法,没啥好介绍的,有兴趣可以看源码
Timer字面意思即定时器,为jkd自带的工具类,提供定时执行任务的相关功能
实际上包括三个类:
TaskQueue
实质上只有一个长度为128的数组用于存储TimerTask
、一个int型变量size表示队列长度、以及对这两个数据的增删改查
TimerThread
实际上就是一个Thread
线程,会不停的监听TaskQueue
,如果队列里面有任务,那么就执行第一个,并将其删除(先删除再执行)
流程分析
Timer
生产任务(实际上是从外部接收到任务),并将任务推到TaskQueue
里面存放,并唤醒TaskQueue
线程(queue.notify()
)TimerThread
监听TaskQueue
,若里面有任务则将其执行并移除队里,若没有任务则让队列等待(queue.wait()
)这么一看,这不就是典型的==生产者/消费者模式==,timer
负责生产(实际上是接受),而TimerThread
负责消费,TaskQueue
作为中转仓库
构造方法
构造的时候会设置定时器线程的名字并将其启动 完整格式如下,其中两个参数均可缺省 public Timer(String name, boolean isDaemon)
"Timer-" + serialNumber()
生成唯一线程名核心方法
核心方法有添加任务、取消任务和净化三种
实际上最后都会使用sched(TimerTask task, long time, long period)
,即指定任务task,在time执行第一次,之后每隔period毫秒执行一次
schedule
使用系统时间计算下一次,即System.currentTimeMillis()+period
而scheduleAtFixedRate
使用本次预计时间计算下一次,即time + period
对于耗时任务,两者区别较大,请按需求选择,瞬时任务无区别
补充
初始化Timer
Timer timer=new Timer();
初始化task
private class MyTask extends TimerTask {
@Override
public void run() {
try {
//do Something
System.out.println(new Date().toString() + ": " + count);
count++;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
添加任务
timer.schedule(myTask, 5000, 3000);
完整代码:
线程应该是最常见的实现方案,创建一个线程执行任务即可,举例几个不同的写法,代码如下
package com.yezi_tool.demo_basic.test;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class ThreadTest {
private Integer count = 0;
public ThreadTest() {
test1();
}
public void test1() {
new Thread(() -> {
while (count < 10) {
System.out.println(new Date().toString() + ": " + count);
count++;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
package com.yezi_tool.demo_basic.test;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Component
public class ThreadTest {
private static final ExecutorService threadPool = Executors.newFixedThreadPool(5);// 线程池
private Integer count = 0;
public ThreadTest() {
test2();
}
public void test2() {
threadPool.execute(() -> {
while (count < 10) {
System.out.println(new Date().toString() + ": " + count);
count++;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
ScheduledTask 有11种添加任务的方法,详情直接查看文件TaskScheduler.java,这里给出常用的几个示例
设置触发频率为3000毫秒
package com.yezi_tool.demo_basic.test;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class ThreadTest {
private Integer count = 0;
private final TaskScheduler taskScheduler;
public ThreadTest(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
test3();
}
public void test3() {
taskScheduler.scheduleAtFixedRate(() -> {
System.out.println(new Date().toString() + ": " + count);
count++;
}, 3000);
}
}
设置触发时间为每天凌晨1点
package com.yezi_tool.demo_basic.test;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class ThreadTest {
private Integer count = 0;
private final TaskScheduler taskScheduler;
public ThreadTest(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
test4();
}
public void test4() {
taskScheduler.schedule(() -> {
System.out.println(new Date().toString() + ": " + count);
count++;
}, new CronTrigger("0 0 1 * * ?"));
}
}
专门整理了一篇quartz的笔记,有兴趣的可以看我上一篇博客
写的并不完善,后续应该会进行修正
https://gitee.com/echo_ye/demo_basic/tree/scheduleDemo
不同定时器启用方法在README.MD
中查看,一共6种方法,如有纰漏请联系我
仅实现了部分功能作为样例,请按照需求自己扩展哦,有疑问或者建议欢迎联系我~
其实除了@schedule,其余的都可以自定义管理器,来统一管理,并动态修改,具体咋做此处先不做赘述
quartz已经整理除了静态定时器和动态定时器,有兴趣的可以瞅瞅
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/156846.html原文链接:https://javaforall.cn