ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ScheduledExecutor.java
Revision: 1.20
Committed: Thu Aug 14 16:48:58 2003 UTC (20 years, 9 months ago) by dl
Branch: MAIN
Changes since 1.19: +40 -7 lines
Log Message:
Added getQueue and shutdownNow javadocs

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain. Use, modify, and
4 * redistribute this code in any way without acknowledgement.
5 */
6
7 package java.util.concurrent;
8 import java.util.concurrent.atomic.*;
9 import java.util.*;
10
11 /**
12 * An <tt>Executor</tt> that can schedule commands to run after a given
13 * delay, or to execute periodically. This class is preferable to
14 * <tt>java.util.Timer</tt> when multiple worker threads are needed,
15 * or when the additional flexibility or capabilities of
16 * <tt>ThreadPoolExecutor</tt> (which this class extends) are
17 * required.
18 *
19 * <p> The <tt>schedule</tt> methods create tasks with various delays
20 * and return a task object that can be used to cancel or check
21 * execution. The <tt>scheduleAtFixedRate</tt> and
22 * <tt>scheduleWithFixedDelay</tt> methods create and execute tasks
23 * that run periodically until cancelled. Commands submitted using
24 * the <tt>execute</tt> method are scheduled with a requested delay of
25 * zero.
26 *
27 * <p> Delayed tasks execute no sooner than they are enabled, but
28 * without any real-time guarantees about when, after they are enabled
29 * they will commence. Tasks tied for the same execution time are
30 * enabled in first-in-first-out (FIFO) order of submission. An
31 * internal {@link DelayQueue} used for scheduling relies on relative
32 * delays, which may drift from absolute times (as returned by
33 * <tt>System.currentTimeMillis</tt>) over sufficiently long periods.
34 *
35 * <p>While this class inherits from {@link ThreadPoolExecutor}, a few
36 * of the inherited tuning methods are not especially useful for
37 * it. In particular, because a <tt>ScheduledExecutor</tt> always acts
38 * as a fixed-sized pool using <tt>corePoolSize</tt> threads and an
39 * unbounded queue, adjustments to <tt>maximumPoolSize</tt> have no
40 * useful effect.
41 *
42 * @since 1.5
43 * @see Executors
44 *
45 * @spec JSR-166
46 * @author Doug Lea
47 */
48 public class ScheduledExecutor extends ThreadPoolExecutor {
49
50 /**
51 * False if should cancel/suppress periodic tasks on shutdown.
52 */
53 private volatile boolean continueExistingPeriodicTasksAfterShutdown;
54
55 /**
56 * False if should cancel non-periodic tasks on shutdown.
57 */
58 private volatile boolean executeExistingDelayedTasksAfterShutdown = true;
59
60
61 /**
62 * Sequence number to break scheduling ties, and in turn to
63 * guarantee FIFO order among tied entries.
64 */
65 private static final AtomicLong sequencer = new AtomicLong(0);
66
67 /**
68 * A delayed or periodic action.
69 */
70 public static class DelayedTask extends CancellableTask implements Delayed {
71 /** Sequence number to break ties FIFO */
72 private final long sequenceNumber;
73 /** The time the task is enabled to execute in nanoTime units */
74 private final long time;
75 /** The delay forllowing next time, or <= 0 if non-periodic */
76 private final long period;
77 /** true if at fixed rate; false if fixed delay */
78 private final boolean rateBased;
79
80 /**
81 * Creates a one-shot action with given nanoTime-based trigger time
82 */
83 DelayedTask(Runnable r, long ns) {
84 super(r);
85 this.time = ns;
86 this.period = 0;
87 rateBased = false;
88 this.sequenceNumber = sequencer.getAndIncrement();
89 }
90
91 /**
92 * Creates a periodic action with given nano time and period
93 */
94 DelayedTask(Runnable r, long ns, long period, boolean rateBased) {
95 super(r);
96 if (period <= 0)
97 throw new IllegalArgumentException();
98 this.time = ns;
99 this.period = period;
100 this.rateBased = rateBased;
101 this.sequenceNumber = sequencer.getAndIncrement();
102 }
103
104
105 public long getDelay(TimeUnit unit) {
106 long d = unit.convert(time - System.nanoTime(),
107 TimeUnit.NANOSECONDS);
108 return d;
109 }
110
111 public int compareTo(Object other) {
112 if (other == this) // compare zero ONLY if same object
113 return 0;
114 DelayedTask x = (DelayedTask)other;
115 long diff = time - x.time;
116 if (diff < 0)
117 return -1;
118 else if (diff > 0)
119 return 1;
120 else if (sequenceNumber < x.sequenceNumber)
121 return -1;
122 else
123 return 1;
124 }
125
126 /**
127 * Return true if this is a periodic (not a one-shot) action.
128 * @return true if periodic
129 */
130 public boolean isPeriodic() {
131 return period > 0;
132 }
133
134 /**
135 * Returns the period, or zero if non-periodic.
136 *
137 * @return the period
138 */
139 public long getPeriod(TimeUnit unit) {
140 return unit.convert(period, TimeUnit.NANOSECONDS);
141 }
142
143 /**
144 * Return a new DelayedTask that will trigger in the period
145 * subsequent to current task, or null if non-periodic
146 * or canceled.
147 */
148 DelayedTask nextTask() {
149 if (period <= 0 || isCancelled())
150 return null;
151 long nextTime = period + (rateBased ? time : System.nanoTime());
152 return new DelayedTask(getRunnable(), nextTime, period, rateBased);
153 }
154 }
155
156 /**
157 * A delayed result-bearing action.
158 */
159 public static class DelayedFutureTask<V> extends DelayedTask implements Future<V> {
160 /**
161 * Creates a Future that may trigger after the given delay.
162 */
163 DelayedFutureTask(Callable<V> callable, long triggerTime) {
164 // must set after super ctor call to use inner class
165 super(null, triggerTime);
166 setRunnable(new InnerCancellableFuture<V>(callable));
167 }
168
169 public V get() throws InterruptedException, ExecutionException {
170 return ((InnerCancellableFuture<V>)getRunnable()).get();
171 }
172
173 public V get(long timeout, TimeUnit unit)
174 throws InterruptedException, ExecutionException, TimeoutException {
175 return ((InnerCancellableFuture<V>)getRunnable()).get(timeout, unit);
176 }
177
178 protected void set(V v) {
179 ((InnerCancellableFuture<V>)getRunnable()).set(v);
180 }
181
182 protected void setException(Throwable t) {
183 ((InnerCancellableFuture<V>)getRunnable()).setException(t);
184 }
185 }
186
187
188 /**
189 * An annoying wrapper class to convince generics compiler to
190 * use a DelayQueue<DelayedTask> as a BlockingQueue<Runnable>
191 */
192 private static class DelayedWorkQueue extends AbstractCollection<Runnable> implements BlockingQueue<Runnable> {
193 private final DelayQueue<DelayedTask> dq = new DelayQueue<DelayedTask>();
194 public Runnable poll() { return dq.poll(); }
195 public Runnable peek() { return dq.peek(); }
196 public Runnable take() throws InterruptedException { return dq.take(); }
197 public Runnable poll(long timeout, TimeUnit unit) throws InterruptedException {
198 return dq.poll(timeout, unit);
199 }
200
201 public boolean add(Runnable x) { return dq.add((DelayedTask)x); }
202 public boolean offer(Runnable x) { return dq.offer((DelayedTask)x); }
203 public void put(Runnable x) throws InterruptedException {
204 dq.put((DelayedTask)x);
205 }
206 public boolean offer(Runnable x, long timeout, TimeUnit unit) throws InterruptedException {
207 return dq.offer((DelayedTask)x, timeout, unit);
208 }
209
210 public Runnable remove() { return dq.remove(); }
211 public Runnable element() { return dq.element(); }
212 public void clear() { dq.clear(); }
213
214 public int remainingCapacity() { return dq.remainingCapacity(); }
215 public boolean remove(Object x) { return dq.remove(x); }
216 public boolean contains(Object x) { return dq.contains(x); }
217 public int size() { return dq.size(); }
218 public boolean isEmpty() { return dq.isEmpty(); }
219 public Iterator<Runnable> iterator() {
220 return new Iterator<Runnable>() {
221 private Iterator<DelayedTask> it = dq.iterator();
222 public boolean hasNext() { return it.hasNext(); }
223 public Runnable next() { return it.next(); }
224 public void remove() { it.remove(); }
225 };
226 }
227 }
228
229 /**
230 * Creates a new ScheduledExecutor with the given initial parameters.
231 *
232 * @param corePoolSize the number of threads to keep in the pool,
233 * even if they are idle.
234 */
235 public ScheduledExecutor(int corePoolSize) {
236 super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
237 new DelayedWorkQueue());
238 }
239
240 /**
241 * Creates a new ScheduledExecutor with the given initial parameters.
242 *
243 * @param corePoolSize the number of threads to keep in the pool,
244 * even if they are idle.
245 * @param threadFactory the factory to use when the executor
246 * creates a new thread.
247 */
248 public ScheduledExecutor(int corePoolSize,
249 ThreadFactory threadFactory) {
250 super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
251 new DelayedWorkQueue(), threadFactory);
252 }
253
254 /**
255 * Creates a new ScheduledExecutor with the given initial parameters.
256 *
257 * @param corePoolSize the number of threads to keep in the pool,
258 * even if they are idle.
259 * @param handler the handler to use when execution is blocked
260 * because the thread bounds and queue capacities are reached.
261 */
262 public ScheduledExecutor(int corePoolSize,
263 RejectedExecutionHandler handler) {
264 super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
265 new DelayedWorkQueue(), handler);
266 }
267
268 /**
269 * Creates a new ScheduledExecutor with the given initial parameters.
270 *
271 * @param corePoolSize the number of threads to keep in the pool,
272 * even if they are idle.
273 * @param threadFactory the factory to use when the executor
274 * creates a new thread.
275 * @param handler the handler to use when execution is blocked
276 * because the thread bounds and queue capacities are reached.
277 */
278 public ScheduledExecutor(int corePoolSize,
279 ThreadFactory threadFactory,
280 RejectedExecutionHandler handler) {
281 super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
282 new DelayedWorkQueue(), threadFactory, handler);
283 }
284
285 /**
286 * Specialized variant of ThreadPoolExecutor.execute for delayed tasks.
287 */
288 private void delayedExecute(Runnable command) {
289 if (isShutdown()) {
290 reject(command);
291 return;
292 }
293 // Prestart a thread if necessary. We cannot prestart it
294 // running the task because the task (probably) shouldn't be
295 // run yet, so thread will just idle until delay elapses.
296 if (getPoolSize() < getCorePoolSize())
297 prestartCoreThread();
298
299 super.getQueue().offer(command);
300 }
301
302 /**
303 * Creates and executes a one-shot action that becomes enabled after
304 * the given delay.
305 * @param command the task to execute.
306 * @param delay the time from now to delay execution.
307 * @param unit the time unit of the delay parameter.
308 * @return a handle that can be used to cancel the task.
309 * @throws RejectedExecutionException if task cannot be scheduled
310 * for execution because the executor has been shut down.
311 */
312
313 public DelayedTask schedule(Runnable command, long delay, TimeUnit unit) {
314 long triggerTime = System.nanoTime() + unit.toNanos(delay);
315 DelayedTask t = new DelayedTask(command, triggerTime);
316 delayedExecute(t);
317 return t;
318 }
319
320 /**
321 * Creates and executes a one-shot action that becomes enabled
322 * after the given date.
323 * @param command the task to execute.
324 * @param date the time to commence excution.
325 * @return a handle that can be used to cancel the task.
326 * @throws RejectedExecutionException if task cannot be scheduled
327 * for execution because the executor has been shut down.
328 */
329 public DelayedTask schedule(Runnable command, Date date) {
330 long triggerTime = System.nanoTime() +
331 TimeUnit.MILLISECONDS.toNanos(date.getTime() -
332 System.currentTimeMillis());
333 DelayedTask t = new DelayedTask(command, triggerTime);
334 delayedExecute(t);
335 return t;
336 }
337
338 /**
339 * Creates and executes a periodic action that becomes enabled first
340 * after the given initial delay, and subsequently with the given
341 * period; that is executions will commence after
342 * <tt>initialDelay</tt> then <tt>initialDelay+period</tt>, then
343 * <tt>initialDelay + 2 * period</tt>, and so on.
344 * @param command the task to execute.
345 * @param initialDelay the time to delay first execution.
346 * @param period the period between successive executions.
347 * @param unit the time unit of the delay and period parameters
348 * @return a handle that can be used to cancel the task.
349 * @throws RejectedExecutionException if task cannot be scheduled
350 * for execution because the executor has been shut down.
351 */
352 public DelayedTask scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
353 long triggerTime = System.nanoTime() + unit.toNanos(initialDelay);
354 DelayedTask t = new DelayedTask(command,
355 triggerTime,
356 unit.toNanos(period),
357 true);
358 delayedExecute(t);
359 return t;
360 }
361
362 /**
363 * Creates a periodic action that becomes enabled first after the
364 * given date, and subsequently with the given period
365 * period; that is executions will commence after
366 * <tt>initialDate</tt> then <tt>initialDate+period</tt>, then
367 * <tt>initialDate + 2 * period</tt>, and so on.
368 * @param command the task to execute.
369 * @param initialDate the time to delay first execution.
370 * @param period the period between commencement of successive
371 * executions.
372 * @param unit the time unit of the period parameter.
373 * @return a handle that can be used to cancel the task.
374 * @throws RejectedExecutionException if task cannot be scheduled
375 * for execution because the executor has been shut down.
376 */
377 public DelayedTask scheduleAtFixedRate(Runnable command, Date initialDate, long period, TimeUnit unit) {
378 long triggerTime = System.nanoTime() +
379 TimeUnit.MILLISECONDS.toNanos(initialDate.getTime() -
380 System.currentTimeMillis());
381 DelayedTask t = new DelayedTask(command,
382 triggerTime,
383 unit.toNanos(period),
384 true);
385 delayedExecute(t);
386 return t;
387 }
388
389 /**
390 * Creates and executes a periodic action that becomes enabled first
391 * after the given initial delay, and and subsequently with the
392 * given delay between the termination of one execution and the
393 * commencement of the next.
394 * @param command the task to execute.
395 * @param initialDelay the time to delay first execution.
396 * @param delay the delay between the termination of one
397 * execution and the commencement of the next.
398 * @param unit the time unit of the delay and delay parameters
399 * @return a handle that can be used to cancel the task.
400 * @throws RejectedExecutionException if task cannot be scheduled
401 * for execution because the executor has been shut down.
402 */
403 public DelayedTask scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
404 long triggerTime = System.nanoTime() + unit.toNanos(initialDelay);
405 DelayedTask t = new DelayedTask(command,
406 triggerTime,
407 unit.toNanos(delay),
408 false);
409 delayedExecute(t);
410 return t;
411 }
412
413 /**
414 * Creates a periodic action that becomes enabled first after the
415 * given date, and subsequently with the given delay between
416 * the termination of one execution and the commencement of the
417 * next.
418 * @param command the task to execute.
419 * @param initialDate the time to delay first execution.
420 * @param delay the delay between the termination of one
421 * execution and the commencement of the next.
422 * @param unit the time unit of the delay parameter.
423 * @return a handle that can be used to cancel the task.
424 * @throws RejectedExecutionException if task cannot be scheduled
425 * for execution because the executor has been shut down.
426 */
427 public DelayedTask scheduleWithFixedDelay(Runnable command, Date initialDate, long delay, TimeUnit unit) {
428 long triggerTime = System.nanoTime() +
429 TimeUnit.MILLISECONDS.toNanos(initialDate.getTime() -
430 System.currentTimeMillis());
431 DelayedTask t = new DelayedTask(command,
432 triggerTime,
433 unit.toNanos(delay),
434 false);
435 delayedExecute(t);
436 return t;
437 }
438
439
440 /**
441 * Creates and executes a Future that becomes enabled after the
442 * given delay.
443 * @param callable the function to execute.
444 * @param delay the time from now to delay execution.
445 * @param unit the time unit of the delay parameter.
446 * @return a Future that can be used to extract result or cancel.
447 * @throws RejectedExecutionException if task cannot be scheduled
448 * for execution because the executor has been shut down.
449 */
450 public <V> DelayedFutureTask<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
451 long triggerTime = System.nanoTime() + unit.toNanos(delay);
452 DelayedFutureTask<V> t = new DelayedFutureTask<V>(callable,
453 triggerTime);
454 delayedExecute(t);
455 return t;
456 }
457
458 /**
459 * Creates and executes a one-shot action that becomes enabled after
460 * the given date.
461 * @param callable the function to execute.
462 * @param date the time to commence excution.
463 * @return a Future that can be used to extract result or cancel.
464 * @throws RejectedExecutionException if task cannot be scheduled
465 * for execution because the executor has been shut down.
466 */
467 public <V> DelayedFutureTask<V> schedule(Callable<V> callable, Date date) {
468 long triggerTime = System.nanoTime() +
469 TimeUnit.MILLISECONDS.toNanos(date.getTime() -
470 System.currentTimeMillis());
471 DelayedFutureTask<V> t = new DelayedFutureTask<V>(callable,
472 triggerTime);
473 delayedExecute(t);
474 return t;
475 }
476
477 /**
478 * Execute command with zero required delay. This has effect
479 * equivalent to <tt>schedule(command, 0, anyUnit)</tt>. Note
480 * that inspections of the queue and of the list returned by
481 * <tt>shutdownNow</tt> will access the zero-delayed
482 * <tt>DelayedTask</tt>, not the <tt>command</tt> itself.
483 *
484 * @param command the task to execute
485 * @throws RejectedExecutionException at discretion of
486 * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted
487 * for execution because the executor has been shut down.
488 */
489 public void execute(Runnable command) {
490 schedule(command, 0, TimeUnit.NANOSECONDS);
491 }
492
493
494 /**
495 * Set policy on whether to continue executing existing periodic
496 * tasks even when this executor has been <tt>shutdown</tt>. In
497 * this case, these tasks will only terminate upon
498 * <tt>shutdownNow</tt>, or after setting the policy to
499 * <tt>false</tt> when already shutdown. This value is by default
500 * false.
501 * @param value if true, continue after shutdown, else don't.
502 */
503 public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) {
504 continueExistingPeriodicTasksAfterShutdown = value;
505 if (!value && isShutdown())
506 cancelUnwantedTasks();
507 }
508
509 /**
510 * Get the policy on whether to continue executing existing
511 * periodic tasks even when this executor has been
512 * <tt>shutdown</tt>. In this case, these tasks will only
513 * terminate upon <tt>shutdownNow</tt> or after setting the policy
514 * to <tt>false</tt> when already shutdown. This value is by
515 * default false.
516 * @return true if will continue after shutdown.
517 */
518 public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() {
519 return continueExistingPeriodicTasksAfterShutdown;
520 }
521
522 /**
523 * Set policy on whether to execute existing delayed
524 * tasks even when this executor has been <tt>shutdown</tt>. In
525 * this case, these tasks will only terminate upon
526 * <tt>shutdownNow</tt>, or after setting the policy to
527 * <tt>false</tt> when already shutdown. This value is by default
528 * true.
529 * @param value if true, execute after shutdown, else don't.
530 */
531 public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) {
532 executeExistingDelayedTasksAfterShutdown = value;
533 if (!value && isShutdown())
534 cancelUnwantedTasks();
535 }
536
537 /**
538 * Set policy on whether to execute existing delayed
539 * tasks even when this executor has been <tt>shutdown</tt>. In
540 * this case, these tasks will only terminate upon
541 * <tt>shutdownNow</tt>, or after setting the policy to
542 * <tt>false</tt> when already shutdown. This value is by default
543 * true.
544 * @return true if will execute after shutdown.
545 */
546 public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() {
547 return executeExistingDelayedTasksAfterShutdown;
548 }
549
550 /**
551 * Cancel and clear the queue of all tasks that should not be run
552 * due to shutdown policy.
553 */
554 private void cancelUnwantedTasks() {
555 boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();
556 boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();
557 if (!keepDelayed && !keepPeriodic)
558 super.getQueue().clear();
559 else if (keepDelayed || keepPeriodic) {
560 Object[] entries = super.getQueue().toArray();
561 for (int i = 0; i < entries.length; ++i) {
562 DelayedTask t = (DelayedTask)entries[i];
563 if (t.isPeriodic()? !keepPeriodic : !keepDelayed)
564 t.cancel(false);
565 }
566 entries = null;
567 purge();
568 }
569 }
570
571 /**
572 * Initiates an orderly shutdown in which previously submitted
573 * tasks are executed, but no new tasks will be accepted. If the
574 * <tt>ExecuteExistingDelayedTasksAfterShutdownPolicy</tt> has
575 * been set <tt>false</tt>, existing delayed tasks whose delays
576 * have not yet elapsed are cancelled. And unless the
577 * <tt>ContinueExistingPeriodicTasksAfterShutdownPolicy</tt> hase
578 * been set <tt>true</tt>, future executions of existing periodic
579 * tasks will be cancelled.
580 */
581 public void shutdown() {
582 cancelUnwantedTasks();
583 super.shutdown();
584 }
585
586 /**
587 * Attempts to stop all actively executing tasks, halts the
588 * processing of waiting tasks, and returns a list of the tasks that were
589 * awaiting execution.
590 *
591 * <p>There are no guarantees beyond best-effort attempts to stop
592 * processing actively executing tasks. This implementations
593 * cancels via {@link Thread#interrupt}, so if any tasks mask or
594 * fail to respond to interrupts, they may never terminate.
595 *
596 * @return list of tasks that never commenced execution. Each
597 * element of this list is a <tt>DelayedTask</tt>, including those
598 * tasks submitted using <tt>execute</tt> which are for scheduling
599 * purposes used as the basis of a zero-delay <tt>DelayedTask</tt>.
600 */
601 public List shutdownNow() {
602 return super.shutdownNow();
603 }
604
605 /**
606 * Removes this task from internal queue if it is present, thus
607 * causing it not to be run if it has not already started. This
608 * method may be useful as one part of a cancellation scheme.
609 *
610 * @param task the task to remove
611 * @return true if the task was removed
612 */
613 public boolean remove(Runnable task) {
614 if (task instanceof DelayedTask && super.getQueue().remove(task))
615 return true;
616
617 // The task might actually have been wrapped as a DelayedTask
618 // in execute(), in which case we need to maually traverse
619 // looking for it.
620
621 DelayedTask wrap = null;
622 Object[] entries = super.getQueue().toArray();
623 for (int i = 0; i < entries.length; ++i) {
624 DelayedTask t = (DelayedTask)entries[i];
625 Runnable r = t.getRunnable();
626 if (task.equals(r)) {
627 wrap = t;
628 break;
629 }
630 }
631 entries = null;
632 return wrap != null && super.getQueue().remove(wrap);
633 }
634
635
636 /**
637 * Returns the task queue used by this executor. Each element
638 * of this queue is a <tt>DelayedTask</tt>, including those
639 * tasks submitted using <tt>execute</tt> which are for
640 * scheduling purposes used as the basis of a zero-delay
641 * <tt>DelayedTask</tt>.
642 *
643 * @return the task queue
644 */
645 public BlockingQueue<Runnable> getQueue() {
646 return super.getQueue();
647 }
648
649 /**
650 * If executed task was periodic, cause the task for the next
651 * period to execute.
652 * @param r the task (assumed to be a DelayedTask)
653 * @param t the exception
654 */
655 protected void afterExecute(Runnable r, Throwable t) {
656 super.afterExecute(r, t);
657 DelayedTask next = ((DelayedTask)r).nextTask();
658 if (next != null &&
659 (!isShutdown() ||
660 (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
661 !isTerminating())))
662 super.getQueue().offer(next);
663
664 // This might have been the final executed delayed task. Wake
665 // up threads to check.
666 else if (isShutdown())
667 interruptIdleWorkers();
668 }
669 }