JAVA并发编程之——定时线程池
来源:互联网 发布:时间的玫瑰 北岛 知乎 编辑:程序博客网 时间:2024/06/11 12:03
在JAVA并发编程之线程池的最后我们讲到了创建定时线程池,其实线程池的创建仍旧是使用的ThreadPoolExcutor的构造函数,具体代码如下:
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到底如何定时完成任务之前,先看下它的结构:
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {}
继承了ThreadPoolExecutor(参考JAVA并发编程之线程池)并实现了ScheduledExecutorService接口,
public interface ScheduledExecutorService extends ExecutorService { /** * 创建一个在指定延迟时间delay后执行的任务 */ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); /** * 同上,这里是callable对象 */ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); /** * 创建一个周期性的延迟任务,在给定的延迟时间initialDelay初次执行 * 之后周期性的执行(每隔period时间执行一次) * 如果执行任务时间大于周期,则下一个任务开始时间会推迟, */ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); /** * 创建在初始延迟后执行并周期性调用的的任务。 * 和上面的方法区别是:delay是前一个任务执行完成后和下一个任务开始时间的间隔 */ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);}
从上面的ScheduledExecutorService接口中我们可以知道定时任务的用法,下面我们我们具体看一下ScheduledThreadPoolExecutor中的实现:
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); 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) { if (callable == null || unit == null) throw new NullPointerException(); RunnableScheduledFuture<V> t = decorateTask(callable, new ScheduledFutureTask<V>(callable, triggerTime(delay, unit))); delayedExecute(t); return t;}public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (period <= 0) throw new IllegalArgumentException(); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(period)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t;}public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { if (command == null || unit == null) throw new NullPointerException(); if (delay <= 0) throw new IllegalArgumentException(); ScheduledFutureTask<Void> sft = new ScheduledFutureTask<Void>(command, null, triggerTime(initialDelay, unit), unit.toNanos(-delay)); RunnableScheduledFuture<Void> t = decorateTask(command, sft); sft.outerTask = t; delayedExecute(t); return t;}
从代码的实现中我们可以知道,传进来的Runnable或Callable接口接口的子类,将它们包装到ScheduledFutureTask中,然后把包装的ScheduledFutureTask放到线程池中执行。这里我们看一下ScheduledFutureTask的构造函数,它是ScheduledThreadPoolExecutor的内部类:
private class ScheduledFutureTask<V> extends FutureTask<V> implements RunnableScheduledFuture<V> { /** 序列号sequenceNumber */ private final long sequenceNumber; /** 任务执行的时间,单位:纳秒 */ private long time; /** * 执行周期,单位:纳秒。正值表示固定频率执行。负值表示固定延迟执行。值0表示一个非重复性的任务 */ private final long period; /** 排队的实际任务 */ RunnableScheduledFuture<V> outerTask = this; /** * delay queue中的索引 */ int heapIndex; //只执行一次的任务 ScheduledFutureTask(Runnable r, V result, long ns) { super(r, result); this.time = ns; this.period = 0; this.sequenceNumber = sequencer.getAndIncrement(); } //周期执行任务 ScheduledFutureTask(Runnable r, V result, long ns, long period) { super(r, result); this.time = ns; this.period = period; this.sequenceNumber = sequencer.getAndIncrement(); } //只执行一次的任务 ScheduledFutureTask(Callable<V> callable, long ns) { super(callable); this.time = ns; this.period = 0; this.sequenceNumber = sequencer.getAndIncrement(); }}
我们已经知道如何把Runnable包装成一个定时任务的,下面我们看一下这个任务是如何被延迟提交的
long triggerTime(long delay) { return now() + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));}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 ensurePrestart(); }}void ensurePrestart() { int wc = workerCountOf(ctl.get()); if (wc < corePoolSize) addWorker(null, true); else if (wc == 0) addWorker(null, false);}
如果线程池没有关闭,检查当前线程池已启动的线程数,是否达到corePoolSize,没有的话,新建一个线程并启动它。注意,这个时候新建的线程是没有持有任何Runnable对象的,它是在启动后到queue(工作队列)中去取出任务执行。
任务的执行
在上面我们完成了任务的添加,在之前的文章中我们知道,任务的执行方法是调用的本身的run()方法,下面看一下
ScheduledFutureTask的run()
public void run() { boolean periodic = isPeriodic(); // 检查当前线程池状态是否需要取消 if (!canRunInCurrentRunState(periodic)) cancel(false); else if (!periodic)// 如果不是周期性任务,直接调用父类FutureTask的run方法执行任务。 ScheduledFutureTask.super.run(); else //调用父类run方法执行任务,但是不设置结果,把future设置成初始化状态 if (ScheduledFutureTask.super.runAndReset()) { //设置任务下次执行时间 setNextRunTime(); // reExecutePeriodic(outerTask); }}
相信到这里你一定不会有恍然大悟的感觉,而是真是++了狗了,哪里有延迟的操作了,因此我们必须看一下DelayedWorkQueue中的任务是如何取出来的。
DelayedWorkQueue
这里主要是讲延迟操作任务,不会详细的讲解DelayedWorkQueue,在上面的分析中我们知道,worker的firstTask=null,因此任务要从队列中去取,下面我们看一下DelayedWorkQueue的take()方法:
public RunnableScheduledFuture<?> take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { RunnableScheduledFuture<?> first = queue[0]; if (first == null) available.await(); else { long delay = first.getDelay(NANOSECONDS); if (delay <= 0) return finishPoll(first); first = null; // don't retain ref while waiting if (leader != null) available.await(); else { Thread thisThread = Thread.currentThread(); leader = thisThread; try { available.awaitNanos(delay); } finally { if (leader == thisThread) leader = null; } } } } } finally { if (leader == null && queue[0] != null) available.signal(); lock.unlock(); }}
这段代码我们就能明白任务是如何延迟执行的,我们取任务的时候会看一下任务的时间与当前时间的差值,如果小于0 证明任务已经到了执行的时间,任务取出执行,如果时间未到,则继续等待(等待和Condition有关,待学习)。
- JAVA并发编程之——定时线程池
- JAVA并发编程之——线程池
- 【Java并发编程】之十九:并发新特性—Executor框架与线程池
- Java并发编程之线程池
- Java并发编程实践笔记之—线程
- java并发编程基础(1)—线程之基本概念
- Java并发之——线程池
- 【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)
- 【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)
- 【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)
- 【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)(r)
- 【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)
- Java并发编程之十九:并发新特性—Executor框架与线程池(含代码)
- Java并发编程——线程中断
- 【Java并发编程实践】— 线程安全
- Java并发编程——线程池初步
- 《java并发编程实战》读书笔记——线程池
- 线程池——JAVA并发编程指南
- java8 Optional防止空指针异常初探
- 卡辛斯基的警告
- 《笨方法学python》习题43的学习笔记
- cx_oracle: UnicodeDecodeError:invalid continuation byte
- c语言顺序程序设计
- JAVA并发编程之——定时线程池
- 分布式与集群的区别
- OKhttp3的基本使用
- 正则表达式的简单使用
- FALSE/TRUE与false/true的区别
- 在Angular组件中引入外部样式
- hdu 4336(状压+期望dp)
- Hibernate框架
- 148. Sort List(链表的归并排序,用快慢指针来partition)