原理
把需要执行的定时任务的具体时间放在时钟对应的刻度,时钟指针每指一个刻度代表该时间点的定时任务全部需要执行,然后全部提交到线程池异步执行。
如果定时任务的时间范围跨度大,可以将时间轮修改为周期时间轮或者分层时间轮。

代码
这里实现的时间轮是周期时间轮,时间刻度为秒
package org.yc.excute.thread;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description: 计划性任务
* @Date: 2023/4/2
**/
public class Timer {
private static final AtomicInteger ROUND = new AtomicInteger(-1);
private static final AtomicInteger POINTER = new AtomicInteger(-1);
private static final LocalDateTime TIMER_START_DATE_TIME = LocalDateTime.now();
private static final Map<Integer, List<Task>> TASKS = new ConcurrentHashMap<>();
private static final int CLOCK_SIZE = 60;//时钟周期大小,默认60秒
private static final Timer TIMER = new Timer();
private Timer() {
start();
}
public static Timer getInstance() {
return TIMER;
}
private void start() {
//ThreadPool是一个自定义线程池
ThreadPool.submit(() -> {
while (true) {
int time = LocalTime.now().getSecond();
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
POINTER.set((POINTER.incrementAndGet()) % CLOCK_SIZE);
if (POINTER.get() % CLOCK_SIZE == 0) {
ROUND.incrementAndGet();
}
//任务数量不能过多,遍历检查任务的时间不能超过1s,否则会出现时间上的误差
//一个时刻的任务数量大概不要超过40000000,这个数量根据机器性能而定
checkTasks();
while (Math.abs(LocalTime.now().getSecond() - time) == 0) {
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
private void checkTasks() {
List<Task> tasks = TASKS.get(POINTER.get());
if (tasks != null) {
Iterator<Task> tasksIterator = tasks.iterator();
while (tasksIterator.hasNext()) {
Task task = tasksIterator.next();
if (task.expired()) {
//异步执行
//ThreadPool是一个自定义线程池
ThreadPool.submit(task);
//删除任务
tasksIterator.remove();
if (task.getFrequency() > 0) {
int interval = (int) (LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) -
TIMER_START_DATE_TIME.toEpochSecond(ZoneOffset.of("+8")));
int time = task.getSeconds();
int cycle = (time + interval) / 60;
int slot = (time + interval) % 60;
task.setCycle(cycle);
task.setSlot(slot);
addTask(task);
}
}
}
}
}
public void setDelayTask(Runnable runnable, int seconds) {
Task delayTask = new DelayTask(runnable, seconds);
addTask(delayTask);
}
public void setCycleTask(Runnable runnable, int seconds) {
Task cycleTask = new CycleTask(runnable, seconds, Integer.MAX_VALUE);
addTask(cycleTask);
}
public void setCycleTask(Runnable runnable, int seconds, int frequency) {
Task cycleTask = new CycleTask(runnable, seconds, frequency);
addTask(cycleTask);
}
public void setTimerTask(Task task) {
addTask(task);
}
private void addTask(Task task) {
int slot = task.getSlot();
List<Task> tasks = Optional.ofNullable(TASKS.get(slot)).orElse(new LinkedList<>());
tasks.add(task);
TASKS.put(slot, tasks);
}
public static abstract class Task implements Runnable {
private int cycle;//周期数
private int slot;//对应的时钟刻度
private int frequency = 1;//任务执行次数
private final int seconds;//延迟时间
private final Runnable runnable;//执行的任务
public Task(Runnable runnable, int seconds) {
if (seconds < 0) {
throw new IllegalArgumentException("seconds must >0");
}
this.seconds = seconds;
int interval = (int) (LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) -
TIMER_START_DATE_TIME.toEpochSecond(ZoneOffset.of("+8")));
cycle = (seconds + interval) / 60;
slot = (seconds + interval) % 60;
this.runnable = runnable;
}
public Task(Runnable runnable, int seconds, int frequency) {
if (seconds < 0) {
throw new IllegalArgumentException("seconds must >0");
}
this.frequency = frequency;
this.seconds = seconds;
int interval = (int) (LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8")) -
TIMER_START_DATE_TIME.toEpochSecond(ZoneOffset.of("+8")));
cycle = (seconds + interval) / 60;
slot = (seconds + interval) % 60;
this.runnable = runnable;
}
@Override
public void run() {
runnable.run();
}
public abstract boolean expired();
public int getCycle() {
return cycle;
}
public int getSlot() {
return slot;
}
public void setCycle(int cycle) {
this.cycle = cycle;
}
public void setSlot(int slot) {
this.slot = slot;
}
public int getSeconds() {
return seconds;
}
public int getFrequency() {
return --frequency;
}
@Override
public String toString() {
return "Task{" +
"cycle=" + cycle +
", slot=" + slot +
", runnable=" + runnable.hashCode() +
'}';
}
}
public static class DelayTask extends Task {
public DelayTask(Runnable runnable, int seconds) {
super(runnable, seconds);
}
@Override
public boolean expired() {
if (getCycle() > ROUND.get()) {
return false;
} else return getCycle() != ROUND.get() || getSlot() <= POINTER.get();
}
}
public static class CycleTask extends Task {
public CycleTask(Runnable runnable, int seconds) {
super(runnable, seconds, Integer.MAX_VALUE);
}
public CycleTask(Runnable runnable, int seconds, int frequency) {
super(runnable, seconds, frequency);
}
@Override
public boolean expired() {
int round = ROUND.get();
int pointer = POINTER.get();
if (getCycle() == 0 && pointer >= getSlot()) {
return true;
}
return (round % getCycle() == 0) && (pointer >= getSlot());
}
}
}
测试
Timer timer = Timer.getInstance();
timer.setDelayTask(() -> {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " 6s后的延迟任务执行");
}, 69);
timer.setCycleTask(() -> {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " 4s周期任务执行2次");
}, 4,2);
timer.setCycleTask(() -> {
System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " 2s一次的周期任务执行");
}, 2);
Thread.currentThread().join();

445

被折叠的 条评论
为什么被折叠?



