ThreadPoolExecutor是Executors中一部分功能,下面来介绍另外一部分功能也就是ScheduledThreadPoolExecutor的实现,后者是一个可以在一定延迟时候或者定时进行任务调度的线程池。
Executors其实是个工具类,里面提供了好多静态方法,根据用户选择返回不同的线程池实例。 ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并实现ScheduledExecutorService接口,关于ThreadPoolExecutor的介绍可以参考: http://www.jianshu.com/p/3cc67876375f 线程池队列是DelayedWorkQueue,它是对delayqueue的优化,关于delayqueue参考:http://www.jianshu.com/p/2659eb72134b ScheduledFutureTask是阻塞队列元素是对任务修饰。
Executors的类图结构如下:
ScheduledThreadPoolExecutor的构造函数如下:
//使用改造后的delayqueue.
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
实例
// 任务间以固定时间间隔执行,延迟1s后开始执行任务,任务执行完毕后间隔2s再次执行,任务执行完毕后间隔2s再次执行,依次往复
static void scheduleWithFixedDelay() throws InterruptedException, ExecutionException {
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(10);
ScheduledFuture result = executorService.scheduleWithFixedDelay(new Runnable() {
public void run() {
System.out.println(System.currentTimeMillis());
}
}, 1000, 2000, TimeUnit.MILLISECONDS);
// 由于是定时任务,一直不会返回
result.get();
System.out.println("over");
}
// 相对开始加入任务的时间点固定频率执行:从加入任务开始算1s后开始执行任务,1+2s开始执行,1+2*2s执行,1+n*2s开始执行;
// 但是如果执行任务时间大约2s则不会并发执行后续任务将会延迟。
static void scheduleAtFixedRate() throws InterruptedException, ExecutionException {
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(10);
ScheduledFuture result = executorService.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println(System.currentTimeMillis());
}
}, 1000, 2000, TimeUnit.MILLISECONDS);
// 由于是定时任务,一直不会返回
result.get();
System.out.println("over");
}
// 延迟1s后开始执行,只执行一次,没有返回值
static void scheduleRunable() throws InterruptedException, ExecutionException {
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(10);
ScheduledFuture result = executorService.schedule(new Runnable() {
@Override
public void run() {
System.out.println("gh");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, 1000, TimeUnit.MILLISECONDS);
System.out.println(result.get());
}
// 延迟1s后开始执行,只执行一次,有返回值
static void scheduleCaller() throws InterruptedException, ExecutionException {
ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(10);
ScheduledFuture result = executorService.schedule(new Callable() {
@Override
public String call() throws Exception {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "gh";
}
}, 1000, TimeUnit.MILLISECONDS);
// 阻塞,直到任务执行完成
System.out.print(result.get());
}
源码分析
schedule函数
public ScheduledFuture schedule(Runnable command, long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
//装饰任务,主要实现public long getDelay(TimeUnit unit)和int compareTo(Delayed other)方法
RunnableScheduledFuture t = decorateTask(command,
new ScheduledFutureTask(command, null,
triggerTime(delay, unit)));
//添加任务到延迟队列
delayedExecute(t);
return t;
}
private void delayedExecute(RunnableScheduledFuture task) {
//如果线程池关闭了,则拒绝任务
if (isShutdown())
reject(task);
else {
//添加任务到队列
super.getQueue().add(task);
//再次检查线程池关闭
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
//确保至少一个线程在处理任务,即使核心线程数corePoolSize为0
ensurePrestart();
}
}
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
//增加核心线程数
if (wc < corePoolSize)
addWorker(null, true);
//如果初始化corePoolSize==0,则也添加一个线程。
else if (wc == 0)
addWorker(null, false);
}
上面做的首先吧runnable装饰为delay队列所需要的格式的元素,然后把元素加入到阻塞队列,然后线程池线程会从阻塞队列获取超时的元素任务进行处理,下面看下队列元素如何实现的。
//r为被修饰任务,result=null,ns为当前时间加上delay时间后的
ScheduledFutureTask(Runnable r, V result, long ns) {
super(r, result);
this.time = ns;
this.period = 0;
this.sequenceNumber = sequencer.getAndIncrement();
}
//通过适配器把runnable转换为callable
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
long triggerTime(long delay, TimeUnit unit) {
return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
}
修饰后把当前任务修饰为了delay队列所需元素,下面看下元素的两个重要方法:过期时间计算和元素比较。
过期时间计算//元素过期算法,装饰后时间-当前时间,就是即将过期剩余时间
public long getDelay(TimeUnit unit) {
return unit.convert(time - now(), TimeUnit.NANOSECONDS);
}
元素比较
public int compareTo(Delayed other) {
if (other == this) // compare zero ONLY if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask x = (ScheduledFutureTask)other;
long diff = time - x.time;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else if (sequenceNumber < x.sequenceNumber)
return -1;
else
return 1;
}
long d = (getDelay(TimeUnit.NANOSECONDS) -
other.getDelay(TimeUnit.NANOSECONDS));
return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
}
schedule(Callable callable, long delay, TimeUnit unit)和schedule(Runnable command, long delay,TimeUnit unit)类似。
compareTo作用是在加入元素到dealy队列时候进行比较,需要调整堆让最快要过期的元素放到队首。所以无论什么时候向队列里面添加元素,队首的都是最即将过期的元素。
scheduleWithFixedDelay函数定时调度:相邻任务间时间固定。
public ScheduledFuture scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?