Java API中的定时器
1. 创建Maven项目 暂时不添加依赖
2.创建MyQuartz继承TimerTask类
package cn.icanci.myquartz;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
/**
* @Author: icanci
* @ProjectName: quartz
* @PackageName: cn.icanci.myquartz
* @Date: Created in 2020/2/14 16:17
* @ClassAction: 定时器的基本使用
*/
public class MyQuartz extends TimerTask {
@Override
public void run() {
for (int i = 0; i < 10; i++){
System.out.print(i +" ");
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String format = sdf.format(new Date());
System.out.println(format);
}
}
3.测试自己的定时器
package cn.icanci.myquartz;
import java.util.Timer;
/**
* @Author: icanci
* @ProjectName: quartz
* @PackageName: cn.icanci.myquartz
* @Date: Created in 2020/2/21 10:16
* @ClassAction: 测试
*/
public class TimeTest extends Timer {
public static void main(String[] args) {
Timer timer = new Timer();
MyQuartz myQuartz = new MyQuartz();
//第一个,自定义的TimerTask对象 第二个 在任务开始之后多久触发 单位 毫秒 第三个参数 每隔多久触发一次 单位 毫秒
timer.schedule(myQuartz,2000,1000);
}
}
4.Java API 实现的定时器的原理 就是实现了Runnable接口的安全线程 底层源代码使用 synchronized 修饰的代码块
public abstract class TimerTask implements Runnable {
final Object lock = new Object();
int state = VIRGIN;
static final int VIRGIN = 0;
static final int SCHEDULED = 1;
static final int EXECUTED = 2;
static final int CANCELLED = 3;
long nextExecutionTime;
long period = 0;
protected TimerTask() {
}
public abstract void run();
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
public long scheduledExecutionTime() {
synchronized(lock) {
return (period < 0 ? nextExecutionTime + period
: nextExecutionTime - period);
}
}
}
Timer类
public class Timer {
private final TaskQueue queue = new TaskQueue();
private final TimerThread thread = new TimerThread(queue);
public Timer() {
this("Timer-" + serialNumber());
}
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
public Timer(String name) {
thread.setName(name);
thread.start();
}
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
}
class TaskQueue {
private TimerTask[] queue = new TimerTask[128];
private int size = 0;
int size() {
return size;
}
void add(TimerTask task) {
// Grow backing store if necessary
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);
}
TimerTask getMin() {
return queue[1];
}
TimerTask get(int i) {
return queue[i];
}
void removeMin() {
queue[1] = queue[size];
queue[size--] = null; // Drop extra reference to prevent memory leak
fixDown(1);
}
void quickRemove(int i) {
assert i <= size;
queue[i] = queue[size];
queue[size--] = null; // Drop extra ref to prevent memory leak
}
void rescheduleMin(long newTime) {
queue[1].nextExecutionTime = newTime;
fixDown(1);
}
boolean isEmpty() {
return size==0;
}
void clear() {
// Null out task references to prevent memory leak
for (int i=1; i<=size; i++)
queue[i] = null;
size = 0;
}
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size && j > 0) {
if (j < size &&
queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
j++; // j indexes smallest kid
if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
void heapify() {
for (int i = size/2; i >= 1; i--)
fixDown(i);
}
}
class TimerThread extends Thread {
boolean newTasksMayBeScheduled = true;
private TaskQueue queue;
TimerThread(TaskQueue queue) {
this.queue = queue;
}
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
/**
* The main timer loop. (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
}
在Timer类中还有另外俩类,TaskQueue和TimerThread
TaskQueue是一个队列,初始队列高度为 128 此队列是为了存储 定时任务的,使用 synchronized 修饰 是线程安全的
TimerThread 是定时任务的启动线程 是用来启动定时任务线程的 使用 synchronized 修饰 是线程安全的
定时器本质就是线程
Quartz是和Spring框架一起使用的,所以需要添加以下依赖
org.quartz-scheduler
quartz
2.3.0
org.springframework
spring-context-support
5.0.2.RELEASE
1.Quartz核心架构
- Scheduler 核心调度器
- Job 任务
- JobDetail 任务描述
Tigger 触发器
- 定义Job和JobDetail
- 定义Trigger和上面的Job一起注册到Scheduler中
- Scheduler通过Trigger执行Job
2.使用方法
2.1
创建一个Java类,创建一个普通方法,作为任务处理方法
2.2
配置Job到Spring容器
2.3
将Job类配置到JobDetail
execute
2.4
配置任务调度触发器
2.5
配置调度工厂
Cron表达式
Cron表达式被用来配置CornTiggger实例,Cron的表达式是字符串,实际上是 七子表达式,描述个别细节时间的时间表.这些子表达式是分开的空白代表
- 1.Sceonds
- 2.Minutes
- 3.Hours
- 4.Day-of-Month
- 5.Month
- 6.Day-of-Week
- 7.Year (可选字段)
例子: "0 0 12 ? * WED" 在每星期三12点执行
在线生成 Cron 表达式 http://cron.qqe2.com/
测试类
package cn.icanci.springquartz.schedule;
/**
* @Author: icanci
* @ProjectName: quartz
* @PackageName: cn.icanci.springquartz.schedule
* @Date: Created in 2020/2/22 12:59
* @ClassAction: 任务调度测试
*/
public class Schedule {
public void execute(){
//0/5 * * * * ? 5秒执行一次
System.out.println("Schedule.execute");
}
}