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 触发器
Quartz
定义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点执行
测试类
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");
}
}