import java.time.LocalDateTime;
import java.util.Timer;
import java.util.TimerTask;/*** @author 缘友一世* date 2023/3/7-13:39*/public class MyTriggerTask {public static void main(String[] args) {//TimerTask 定时器任务MyTask myTask = new MyTask();//定时器Timer timer = new Timer();//执行方法:延迟3秒后第一次执行,以后每1秒执行1次timer.schedule(myTask,3000,1000);}static class MyTask extends TimerTask{@Overridepublic void run() {System.out.println("MyTask正在执行的时间"+LocalDateTime.now().toLocalTime());}}
}
//定时器任务队列
private final TaskQueue queue = new TaskQueue();
//定时器线程
private final TimerThread thread = new TimerThread(queue);
- nextExecutionTime:TimerTask中属性,表示下一次开始执行的时间。
class TaskQueue {//TimerTask数组,默认为128个private TimerTask[] queue = new TimerTask[128];//表示优先级队列中的任务数private int size = 0;/*** Returns the number of tasks currently on the queue.*/int size() {return size;}//添加一个新的任务到优先队列中void add(TimerTask task) {// 如果队列已满,则进行扩容if (size + 1 == queue.length)queue = Arrays.copyOf(queue, 2*queue.length);//然后将任务放入队列queue[++size] = task;//队列排序方法,向上提升新任务在队列中的位置fixUp(size);}//返回优先队列中的根节点【头部任务】TimerTask getMin() {return queue[1];}//返回第i个任务TimerTask get(int i) {return queue[i];}//从优先队列中移除头部任务,然后从根节点向下"降级"void removeMin() {queue[1] = queue[size];queue[size--] = null; // Drop extra reference to prevent memory leakfixDown(1);}//快速删除第i个任务,不考虑保持堆不变,不用调整队列void quickRemove(int i) {assert i <= size;queue[i] = queue[size];queue[size--] = null; // Drop extra ref to prevent memory leak}/*** Sets the nextExecutionTime associated with the head task to the* specified value, and adjusts priority queue accordingly.*///设置与头部任务关联的nextExecutionTime为指定值,并调整优先队列void rescheduleMin(long newTime) {queue[1].nextExecutionTime = newTime;fixDown(1);}//判断当前队列是否为空boolean isEmpty() {return size==0;}//从优先队列中移除所有元素void clear() {// Null out task references to prevent memory leakfor (int i=1; i<=size; i++)queue[i] = null;size = 0;}//将queue[k]向上‘提升’,直到queue[k]的nextExecutionTime大于或等于父级的执行时间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;}}//将queue[k]向下‘降级’,直到queue[k]的nextExecutionTime小于或等于子级的执行时间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 kidif (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 {//用于标识当前Timer实例中是否存在任务需要调度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 cancelledsynchronized(queue) {//设置是否存在任务需要调度标识newTasksMayBeScheduled为falsenewTasksMayBeScheduled = false;queue.clear(); // Eliminate obsolete references}}}//主定时器循环private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;//设置同步锁,锁定当前queue对象synchronized(queue) {// 判断当前队列是否为空,如果为空且没有新的调度任务,则线程进入等待状态while (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break; // 如果队列为空,则跳出死循环//当前时间 执行时间 long currentTime, executionTime;//获取任务队列中的头部任务(小顶堆的根节点)task = queue.getMin();//设置同步锁锁定当前执行的任务synchronized(task.lock) {//他们当前任务,如果危险组状态,则从队列中移除if (task.state == TimerTask.CANCELLED) {queue.removeMin();continue; // 有任何操作,作再次轮循队列}//获取当前时间戳,单位为mscurrentTime = System.currentTimeMillis();//获取当前任务的执行时间,单位为msexecutionTime = task.nextExecutionTime;//判断当前任务执行时间是否小于等于当前时间if (taskFired = (executionTime<=currentTime)) {/*period:TimeTask类的属性周期(以毫秒为单位)用于重复任务。正值表示固定速率执行。负值表示固定延迟执行。0表示非重复任务。*///如果当今任务只执行一次,则从队列中移除if (task.period == 0) { queue.removeMin();//设置任务状态为已执行task.state = TimerTask.EXECUTED;} else { // Repeating task, reschedule//如果是重复任务则重新设置nextExecutionTimequeue.rescheduleMin(task.period<0 ? currentTime - task.period: executionTime + task.period);}}}//如果还会到执行时间则设置队列等待时间为executionTime - currentTimeif (!taskFired) queue.wait(executionTime - currentTime);}//如果任务执行时间已到则在当前线程中运行该任务不需要持有锁if (taskFired) task.run();} catch(InterruptedException e) {}}}
}
//默认的构造器
public Timer() {
//以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);
}
//调度指定的任务在指定时间内执行,如果指定的是当前时间,则调度任务立即执行
public void schedule(TimerTask task, Date time) {sched(task, time.getTime(), 0);
}/*在指定的延迟时间之后执行,将重复延迟执行在fixed-delay方式的执行中,每次执行都是相对于上一次执行的实际执行时间来计算的如何执行过程中因任何原因(如垃圾回收或者其他后台活动而延迟),则后续任务的执行也将延迟
*/
public void schedule(TimerTask task, long delay, long period) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, System.currentTimeMillis()+delay, -period);
}
//指定第一次执行时间,将重复延迟执行
public void schedule(TimerTask task, Date firstTime, long period) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, firstTime.getTime(), -period);
}
/*在指定的延迟时间之后执行,将重复地一固定速率执行在fixed-delay方式的执行中,每次执行都是相对于充实执行的时间来计算的如果任务执行过程中因任何原因(如垃圾声音或者其他后台活动)而延迟,则两个或多个执行任务将快速、连续地发生,以追赶延迟的计划固定资源的方式适用于对时间绝对敏感的重复性活动。例如每10秒钟执行一次的提示任务
*/
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, System.currentTimeMillis()+delay, period);
}
//指定第一次执行时间,将以固定速率重复执行
public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");sched(task, firstTime.getTime(), period);
}
/*在指定时间段内的指定时间执行指定的定时任务以后,以毫秒为单位如果period为非零,则重复调度任务;如果period为零,则只调度一次任务
*/
private void sched(TimerTask task, long time, long period) {if (time < 0)throw new IllegalArgumentException("Illegal execution time.");if (Math.abs(period) > (Long.MAX_VALUE >> 1))period >>= 1;//设置同步锁,锁定当前的queue对象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();}
}
public abstract class TimerTask implements Runnable {//对象锁,此对象用于控制对TimerTask内部的访问final Object lock = new Object();//当前任务状态,默认为VIRGINint state = VIRGIN;//当前任务还未被执行static final int VIRGIN = 0;//当前任务已被调度,如果是非重复任务,则还未被调度static final int SCHEDULED = 1;//当前非重复任务已经执行或者正在执行,并且未被删除。static final int EXECUTED = 2;//当前任务已被删除static final int CANCELLED = 3;/*当前任务的下一次执行时间,格式为System.currentTimeMillis()方法返回的毫秒数如果是重复任务,则会在任务执行之前更新*/long nextExecutionTime;/*指定重复任务的间隔毫秒数,重复任务的周期正值表示固定速率执行。负值表示固定延迟执行。0表示非重复任务。*/long period = 0;//构造器protected TimerTask() {}//该任务要实现的操作逻辑 我们在继承TimerTask后需要重写的方法public abstract void run();/*取消此计时器任务,可重复调用,取消场景如下:当前任务为所以重复任务,而且尚未运行或者还未被调度,则它将永远不会再次运行。当前任务为重复任务则他正在进行,浙江等到任务执行完成后,永远不会再次运行。@return true 如果该方法阻止一个或多个计划的执行发生,则返回true*/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);}}
}
java.util.concurrent
包中新增了ScheduledExecutorService
接口,该接口继承于ExecutorService
,因此支持线程池的所有功能。其默认的实现类是java.util.concurrent.ScheduledThreadPoolExecutor
//给定核心线程池之大小,创建ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}
//给定初始参数,创建ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(), threadFactory);
}//给定初始参数,创建ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize,RejectedExecutionHandler handler) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(), handler);
}//给定初始参数,创建ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize,ThreadFactory threadFactory,RejectedExecutionHandler handler) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue(), threadFactory, handler);
}
public interface ScheduledExecutorService extends ExecutorService {
//设置延迟时间的调度,只执行一次,调度之后可通过Future.get()阻塞直至任务执行完毕
public ScheduledFuture> schedule(Runnable command,long delay, TimeUnit unit);
//设置延迟时间的调度,只执行一次,调度之后,阻塞直至任务执行完毕,并且可以返回执行结果
public ScheduledFuture schedule(Callable callable,long delay, TimeUnit unit);
/*设置延迟时间的调度,以固定频率循环执行。即在initialDelay初识延迟后,initialDelay+period执行第一次,initialDelay+2*period,执行第二次。如果执行时间大于延迟时间,则间隔时间为任务执行时间。否则使用上面的公式计算间隔时间
*/
public ScheduledFuture> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
/*设置延迟时间的调度,以固定的延迟时间循环执行任务。不管执行多长时间,都是一个执行任务的终止时间和下一个执行任务的开始时间之间的间隔时间再加上固定的延迟时间为任务的执行时间
*/
public ScheduledFuture> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
}
JDK提供的默认实现类ScheduledThreadPoolExecutor
类同时继承了ThreadPoolExecutor
类,因此ScheduledThreadPoolExecutor
不仅可以延迟执行和周期性循环任务,还具有提交异步任务功能。
在ScheduledThreadPoolExecuto
r类中有两个重要的内部类:ScheduledFutureTask
和DelayedWorkQueue
类。
DelayedWorkQueue
类继承自AbstractQueue
类,并且实现了BlockingQueue
接口, 该类是基于最小堆结构的优先队列,而且是一个阻塞队列,在该类内部包含的成员变量中,有一个RunnableScheduledFuture
类型的队列,初始数量为16
。//特有的延迟队列也是一个有序对列,通过每个任务按照距离下次执行时间的间隔长短来排序
static class DelayedWorkQueue extends AbstractQueue implements BlockingQueue {//基于堆的数据结构,注意所有的对称作必须记录索引更改private static final int INITIAL_CAPACITY = 16;private RunnableScheduledFuture>[] queue =new RunnableScheduledFuture>[INITIAL_CAPACITY];private final ReentrantLock lock = new ReentrantLock();private int size = 0;//主从线程设计,避免了不必要的等待时间,当线程池中的一个线程变成主线程时,它只等待下一个延迟时间,但是其他线程将无限期等待private Thread leader = null;//当队列顶部的新任务变为可用时,发出信号状态private final Condition available = lock.newCondition();//调整堆数组的大小private void grow() {int oldCapacity = queue.length;int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%if (newCapacity < 0) // overflownewCapacity = Integer.MAX_VALUE;queue = Arrays.copyOf(queue, newCapacity);}//将某个元素从队列中移除public boolean remove(Object x) {final ReentrantLock lock = this.lock;lock.lock();try {int i = indexOf(x);if (i < 0)return false;setIndex(queue[i], -1);int s = --size;RunnableScheduledFuture> replacement = queue[s];queue[s] = null;if (s != i) {siftDown(i, replacement);if (queue[i] == replacement)siftUp(i, replacement);}return true;} finally {lock.unlock();}}//返回堆顶的第一个元素public RunnableScheduledFuture> peek() {final ReentrantLock lock = this.lock;lock.lock();try {return queue[0];} finally {lock.unlock();}}//将第一个元素从队列中弹出,如果队列是空就返回nullpublic RunnableScheduledFuture> poll() {final ReentrantLock lock = this.lock;lock.lock();try {RunnableScheduledFuture> first = queue[0];if (first == null || first.getDelay(NANOSECONDS) > 0)return null;elsereturn finishPoll(first);} finally {lock.unlock();}}
ScheduledFutureTask
类继承了FutureTask
类,并实现了RunnableScheduledFuture
接口,表示返回异步任务的结果。/*将任务封装为ScheduledFutureTask对象,基于相对时间,不因系统时间改变而受到影响可以通过返回FutureTask对象来获取执行的结果
*/
private class ScheduledFutureTask extends FutureTask implements RunnableScheduledFuture {//记录任务被添加到ScheduledThreadPoolExecutor中的序号private final long sequenceNumber;//以ns为单位指定下一次任务的执行时间private long time;//以ns为单位指定重复执行任务的执行周期/*正值:执行固定速率负值:固定延迟执行0:非重复任务及非周期性*/private final long period;//由reExecutePeriodic重新排队的任务RunnableScheduledFuture outerTask = this;//延迟队列中索引,支持快速取消任务int heapIndex;ScheduledFutureTask(Runnable r, V result, long ns) {super(r, result);this.time = ns;this.period = 0;this.sequenceNumber = sequencer.getAndIncrement();}
//返回距离下次任务执行时间的间隔
public long getDelay(TimeUnit unit) {//计算距下次执行时间与当前系统时间的差值return unit.convert(time - now(), NANOSECONDS);
}
//比较任务之间的优先级,如果距离下次执行的时间间隔较短,则表示优先级较高
public int compareTo(Delayed other) {if (other == this) //同一个对象返回0return 0;//如果是ScheduledFutureTask类型,则强制转化后为变量time的差值if (other instanceof ScheduledFutureTask) {ScheduledFutureTask> x = (ScheduledFutureTask>)other;long diff = time - x.time;if (diff < 0)return -1;else if (diff > 0)return 1;else if (sequenceNumber < x.sequenceNumber)return -1;elsereturn 1;}//如果不是ScheduledFutureTask类型,则比较getDelay的返回值long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
//覆盖FutureTask的run()方法
public void run() {//判断是否为周期性任务boolean periodic = isPeriodic();//根据当前任务运行状态判断是否删除if (!canRunInCurrentRunState(periodic))cancel(false);//如果是周期性任务,则调用父类的run方法else if (!periodic)ScheduledFutureTask.super.run();//如果任务执行结束,则重置以备下次执行else if (ScheduledFutureTask.super.runAndReset()) {setNextRunTime();reExecutePeriodic(outerTask);}
}
//重新执行周期性任务
void reExecutePeriodic(RunnableScheduledFuture> task) {if (canRunInCurrentRunState(true)) {super.getQueue().add(task);if (!canRunInCurrentRunState(true) && remove(task))task.cancel(false);elseensurePrestart();}
}
schedule
方法源码就不再进行详说。/*** @author 缘友一世* date 2023/3/7-13:39*/public class MyTriggerTask2 {public static void main(String[] args) {ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();//立即执行,任务执行结束后每隔1秒执行一次,真正的执行时间是任务执行时间+间隔时间executorService.scheduleWithFixedDelay(()->{System.out.println("["+LocalDateTime.now()+"] 任务执行中...");},0,1, TimeUnit.SECONDS);System.out.println("任务开始执行!");}}