欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

手动实现Java定时器(类似java.util.Timer)

程序员文章站 2022-06-09 09:39:13
...

一、实现原理

1.将定时任务信息封装为JavaBean,信息包括:执行时间,要执行的任务,是否循环执行

2.优先级队列存储定时任务,执行时间早的优先

3.单线程从队列取任务执行,如果执行时间未到达,则使用 Object.wait() 等待指定的时间

注意:新任务加入时需要notify,否则新任务执行时间早的,将无法被调度。

二、源码

package org.nelin;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;

/**
 * 
 * <p>简单timer: 1) 单线程检测 2) 将任务封装到JavaBean 3) 时间到自动执行
 * 
 * @author ljf
 *
 */
public class Timer {
	/** 定时检测线程 */
	private Thread thread;
	
	/** 任务执行线程池,防止任务长时间执行阻塞检测线程 */
	private ExecutorService taskService = Executors.newCachedThreadPool();
	
	/** 用于等待和唤醒 */
	private Object lock = new Object();
	
	/** 任务队列,时间早的排前面 */
	private PriorityBlockingQueue<ScheduleTask> queue = new PriorityBlockingQueue<>();
	
	/** 是否被shutdown */
	private volatile boolean shutdowned = false;
	
	/**
	 *	检测执行时间是否到达:
	 * 	1.从队列拿到任务的时候,时间到了,直接执行
	 * 	2.时间没到,阻塞直到时间到达
	 * 	3.阻塞期间,被唤醒,说明有新任务到达,此时检测时间是否到了,没到放回队列里面
	 * 	4.执行完任务,检测是否为循环执行的任务,是则改变触发时间,添加回队列
	 */
	private Runnable checkTimeTask = () -> {
		while (true) {
			if (shutdowned) {
				break;
			}
			ScheduleTask task = null;
			try {
				task = queue.take();
			} catch (InterruptedException e1) {
				break;
			}
			boolean hasExecuted = true;
			long currentTime = System.currentTimeMillis();
			if (task.time <= currentTime) {
				taskService.execute(task.task);
			} else {
				synchronized (lock) {
					try {
						lock.wait(task.time - currentTime);
					} catch (InterruptedException e) {
						break;
					}
				}
				if (System.currentTimeMillis() < task.time) {
					//说明有新任务进来
					hasExecuted = false;
					queue.add(task);
				} else {
					taskService.execute(task.task);
				}
			}
			if (hasExecuted && task.period > 0) {
				task.time += task.period;
				queue.add(task);
			}
		}
		taskService.shutdown();
	};
	
	public void start() {
		thread = new Thread(checkTimeTask);
		thread.start();
	}
	
	public void shutdown() {
		shutdowned = true;
		thread.interrupt();
	}

	public void schedule(long delay, Runnable task) {
		scheduleAtFixedRate(delay, 0, task);
	}

	public void scheduleAtFixedRate(long delay, long period, Runnable task) {
		checkShutdown();
		queue.add(new ScheduleTask(System.currentTimeMillis() + delay, task, period));
		synchronized (lock) {
			lock.notify(); //notify防止新任务被延误
		}
	}

	public void scheduleAtTime(long time, Runnable task) {
		checkTime(time);
		checkShutdown();
		queue.add(new ScheduleTask(time, task, 0));
		synchronized (lock) {
			lock.notify();
		}
	}
	
	private void checkShutdown() {
		if (shutdowned) {
			throw new IllegalStateException("timer is shutdowned,can't submit task!");
		}
	}
	
	private void checkTime(long time) {
		if (time > System.currentTimeMillis()) {
			throw new IllegalArgumentException("time illegal, is passed!");
		}
	}
	
	/**
	 * 	定时任务信息封装
	 */
	private static class ScheduleTask implements Comparable<ScheduleTask> {
		long time;
		final Runnable task;
		final long period;

		public ScheduleTask(long time, Runnable task, long period) {
			this.time = time;
			this.task = task;
			this.period = period;
		}

		@Override
		public int compareTo(ScheduleTask o) {
			return (int)(time - o.time);
		}
		
	}
	
	public static void main(String[] args) {
		Timer timer = new Timer();
		timer.start();
		timer.schedule(3000, () -> {
			System.out.println("three seconds later");
		});
		timer.scheduleAtFixedRate(1000, 2000, () -> {
			System.out.println("one second later, 2 seconds period");
		});
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("going to shutdown");
		timer.shutdown();
	}
}

执行效果如下:

手动实现Java定时器(类似java.util.Timer)