计算机系统应用教程网站

网站首页 > 技术文章 正文

ScheduledThreadPoolExecutor源码解读(一)

btikc 2024-11-21 10:54:11 技术文章 20 ℃ 0 评论

Java实现定时任务有一下几种基本的方法:

1、Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。

2、ScheduledExecutorService:也jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

3、Spring Task:Spring3.0以后自带的task,其实它的底层实现也是ScheduledExecutorService。

ScheduledExecutorService是一个接口,它的比较常用的一个实现就是ScheduledThreadPoolExecutor。

今天我们就来解读一下ScheduledThreadPoolExecutor的源码。

构造方法

public ScheduledThreadPoolExecutor(int corePoolSize) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue());
}
 
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), threadFactory);
}
 
public ScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), handler);
}
 
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
	super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
		  new DelayedWorkQueue(), threadFactory, handler);
}

因为ScheduledThreadPoolExecutor类是ThreadPoolExecutor类的子类,所以ScheduledThreadPoolExecutor类的构造方法实际上调用的是ThreadPoolExecutor类的构造方法。

schedule方法

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
	//如果传递的Runnable对象和TimeUnit时间单位为空
	//抛出空指针异常
	if (command == null || unit == null)
		throw new NullPointerException();
	//装饰任务对象,在decorateTask方法中直接返回ScheduledFutureTask对象
	RunnableScheduledFuture<?> t = decorateTask(command, 
                                              new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit)));
	//执行延时任务
	delayedExecute(t);
	//返回任务
	return t;
}
 
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) 
	//如果传递的Callable对象和TimeUnit时间单位为空
	//抛出空指针异常
	if (callable == null || unit == null)
		throw new NullPointerException();
	//装饰任务对象,在decorateTask方法中直接返回ScheduledFutureTask对象
	RunnableScheduledFuture<V> t = decorateTask(callable,
		new ScheduledFutureTask<V>(callable, triggerTime(delay, unit)));
	//执行延时任务
	delayedExecute(t);
	//返回任务
	return t;
}

schedule的2个方法接收参数不同,内部逻辑都是一样,通过triggerTime计算触发时间,然后新建ScheduledFutureTask,并且经过decorateTask进行装饰后,调用delayedExecute进行延迟执行。

triggerTime方法

// 根据延迟和单位计算延迟时间
private long triggerTime(long delay, TimeUnit unit) {
  // unit.toNanos 方法出来最大值是 Long.MAX
	return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
}
 // 根据延迟值计算触发时间
long triggerTime(long delay) {
	return now() +
		((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
// 防止延迟过大
private long overflowFree(long delay) {
	//获取队列中的节点
	Delayed head = (Delayed) super.getQueue().peek();
	//获取的节点不为空,则进行后续处理
	if (head != null) {
		//从队列节点中获取延迟时间
		long headDelay = head.getDelay(NANOSECONDS);
		//如果从队列中获取的延迟时间小于0,并且传递的delay
		//值减去从队列节点中获取延迟时间小于0
		if (headDelay < 0 && (delay - headDelay < 0))
			//将delay的值设置为Long.MAX_VALUE + headDelay
			delay = Long.MAX_VALUE + headDelay;
	}
	//返回延迟时间
	return delay;
}

这边需要关注的是overflowFree方法,因为ScheduledThreadPoolExecutor使用的DelayedWorkQueue,是根据time的大小进行排序。如果当前列表头(即将执行的那个任务,暂时称呼为head)的time = now() - 100,而新增的任务(暂时称呼为add)的time = Long.MAX + now(),那么 add.time - head.time 溢出变成了负数,间接导致列表的排序异常。overflowFree保证了这边不会发生溢出,还是刚才的例子,head.time = now() - 100,head.delay = -100,满足了if条件,那么这个时候,add.delay = Long.MAX_VALUE - 100,add.time = Long.MAX_VALUE - 100 + now(),add.time - head.time = Long.MAX_VALUE,完美解决了溢出的问题。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表