All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.keayuan.util.Timer Maven / Gradle / Ivy

The newest version!
package cn.keayuan.util;

import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by keayuan on 2024.10.14.
 *
 * @author keayuan
 */
public final class Timer {

    private final Queue queue = new Queue();
    private final Thread thread = new Thread(this::loop, "SSK-Timer");

    private static final Timer instance = new Timer();

    private Timer() {
        thread.setDaemon(true);
        thread.setPriority(Thread.MAX_PRIORITY);
        thread.start();
    }

    private void loop() {
        try {
            while (true) {
                Task task = queue.get();
                task.run();
            }
        } catch (InterruptedException ignored) {
        }
    }

    public static long currentTime() {
        return System.nanoTime() / 1000000;
    }

    public static Task add(Runnable runnable) {
        return add(null, runnable);
    }

    public static Task add(long delay, Runnable runnable) {
        return add(delay, null, runnable);
    }

    public static Task add(Executor executor, Runnable runnable) {
        return add(0, executor, runnable);
    }

    public static Task add(long delay, Executor executor, Runnable runnable) {
        Task task = new Task(currentTime() + delay, runnable, executor);
        instance.queue.addTask(task);
        return task;
    }

    public static TaskBuilder builder(Runnable runnable) {
        return new TaskBuilder(runnable);
    }

    public static class Task {
        private long when;
        private volatile boolean isCancel;
        private final Runnable runnable;
        private final Executor executor;
        private final long period;
        private final AtomicInteger count;

        private Task next;

        private Task(TaskBuilder builder) {
            runnable = builder.runnable;
            this.when = currentTime() + builder.delay;
            period = builder.period;
            count = new AtomicInteger(builder.count);
            executor = builder.executor;
        }

        private Task(long when, Runnable runnable, Executor executor) {
            this.when = when;
            this.runnable = runnable;
            this.executor = executor;
            period = 0;
            count = new AtomicInteger(1);
        }

        boolean isExpired() {
            return currentTime() >= when;
        }

        long remaining() {
            return when - currentTime();
        }

        public void cancel() {
            isCancel = true;
        }

        void run() {
            if (isCancel) return;
            if (executor == null) realRun();
            else executor.execute(this::realRun);
        }

        void realRun() {
            if (isCancel) return;
            try {
                runnable.run();
            } catch (Exception ignored) {
            } finally {
                if (count.decrementAndGet() != 0) {
                    when += period;
                    instance.queue.addTask(this);
                }
            }
        }
    }

    public static class TaskBuilder {
        private final Runnable runnable;
        private Executor executor;
        private long delay;
        private long period;
        private int count = 1;

        public TaskBuilder(Runnable runnable) {
            if (runnable == null) throw new IllegalArgumentException("runnable can not be null");
            this.runnable = runnable;
        }

        public TaskBuilder executor(Executor executor) {
            this.executor = executor;
            return this;
        }

        public TaskBuilder delay(long delay) {
            this.delay = delay;
            return this;
        }

        public TaskBuilder period(long period, int count) {
            if (period < 20) throw new IllegalArgumentException("period cannot be less than 20");
            if (count == 0) throw new IllegalArgumentException("count cannot be equals 0");
            this.period = period;
            this.count = count;
            return this;
        }

        public TaskBuilder period(long period) {
            return period(period, -1);
        }

        public Task start() {
            Task task = new Task(this);
            instance.queue.addTask(task);
            return task;
        }
    }

    private static class Queue {
        Task head;
        private final ReentrantLock lock = new ReentrantLock();
        private final Condition notEmpty = lock.newCondition();

        void addTask(Task task) {
            if (task.runnable == null || task.isCancel || task.count.get() == 0) return;
            lock.lock();
            try {
                if (head == null) {
                    head = task;
                    notEmpty.signal();
                    return;
                }
                if (task.when < head.when) {
                    task.next = head;
                    head = task;
                    notEmpty.signal();
                    return;
                }
                Task pre = head;
                while (pre.next != null && pre.next.when < task.when) pre = pre.next;
                Task temp = pre.next;
                pre.next = task;
                task.next = temp;
                notEmpty.signal();
            } finally {
                lock.unlock();
            }
        }

        Task get() throws InterruptedException {
            lock.lock();
            try {
                if (head == null) notEmpty.await();
                if (!head.isExpired()) {
                    notEmpty.await(head.remaining(), TimeUnit.MILLISECONDS);
                    return get();
                }
                Task task = head;
                head = head.next;
                return task;
            } finally {
                lock.unlock();
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy