Java实现时间轮算法

原理

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

代码

这里实现的时间轮是周期时间轮,时间刻度为秒

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();

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值