ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/main/java/util/concurrent/ScheduledExecutor.java
Revision: 1.21
Committed: Tue Aug 19 15:04:57 2003 UTC (20 years, 9 months ago) by tim
Branch: MAIN
Changes since 1.20: +57 -59 lines
Log Message:
Future extends Cancellable.
class SE.DelayedTask -> interface ScheduledCancellable
class SE.DelayedFutureTask -> interface ScheduledFuture
Add primitive SE test.
Related internals changes.

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 private static class ScheduledCancellableTask
68 extends CancellableTask implements ScheduledCancellable {
69
70 /** Sequence number to break ties FIFO */
71 private final long sequenceNumber;
72 /** The time the task is enabled to execute in nanoTime units */
73 private final long time;
74 /** The delay forllowing next time, or <= 0 if non-periodic */
75 private final long period;
76 /** true if at fixed rate; false if fixed delay */
77 private final boolean rateBased;
78
79 /**
80 * Creates a one-shot action with given nanoTime-based trigger time
81 */
82 ScheduledCancellableTask(Runnable r, long ns) {
83 super(r);
84 this.time = ns;
85 this.period = 0;
86 rateBased = false;
87 this.sequenceNumber = sequencer.getAndIncrement();
88 }
89
90 /**
91 * Creates a periodic action with given nano time and period
92 */
93 ScheduledCancellableTask(Runnable r, long ns, long period, boolean rateBased) {
94 super(r);
95 if (period <= 0)
96 throw new IllegalArgumentException();
97 this.time = ns;
98 this.period = period;
99 this.rateBased = rateBased;
100 this.sequenceNumber = sequencer.getAndIncrement();
101 }
102
103
104 public long getDelay(TimeUnit unit) {
105 long d = unit.convert(time - System.nanoTime(),
106 TimeUnit.NANOSECONDS);
107 return d;
108 }
109
110 public int compareTo(Object other) {
111 if (other == this) // compare zero ONLY if same object
112 return 0;
113 ScheduledCancellableTask x = (ScheduledCancellableTask)other;
114 long diff = time - x.time;
115 if (diff < 0)
116 return -1;
117 else if (diff > 0)
118 return 1;
119 else if (sequenceNumber < x.sequenceNumber)
120 return -1;
121 else
122 return 1;
123 }
124
125 /**
126 * Return true if this is a periodic (not a one-shot) action.
127 * @return true if periodic
128 */
129 public boolean isPeriodic() {
130 return period > 0;
131 }
132
133 /**
134 * Returns the period, or zero if non-periodic.
135 *
136 * @return the period
137 */
138 public long getPeriod(TimeUnit unit) {
139 return unit.convert(period, TimeUnit.NANOSECONDS);
140 }
141
142 /**
143 * Return a new ScheduledCancellable that will trigger in the period
144 * subsequent to current task, or null if non-periodic
145 * or canceled.
146 */
147 ScheduledCancellableTask nextTask() {
148 if (period <= 0 || isCancelled())
149 return null;
150 long nextTime = period + (rateBased ? time : System.nanoTime());
151 return new ScheduledCancellableTask(getRunnable(), nextTime, period, rateBased);
152 }
153 }
154
155 private static class ScheduledFutureTask<V>
156 extends ScheduledCancellableTask implements ScheduledFuture<V> {
157
158 /**
159 * Creates a ScheduledFuture that may trigger after the given delay.
160 */
161 ScheduledFutureTask(Callable<V> callable, long triggerTime) {
162 // must set after super ctor call to use inner class
163 super(null, triggerTime);
164 setRunnable(new InnerCancellableFuture<V>(callable));
165 }
166
167 public V get() throws InterruptedException, ExecutionException {
168 return ((InnerCancellableFuture<V>)getRunnable()).get();
169 }
170
171 public V get(long timeout, TimeUnit unit)
172 throws InterruptedException, ExecutionException, TimeoutException {
173 return ((InnerCancellableFuture<V>)getRunnable()).get(timeout, unit);
174 }
175
176 protected void set(V v) {
177 ((InnerCancellableFuture<V>)getRunnable()).set(v);
178 }
179
180 protected void setException(Throwable t) {
181 ((InnerCancellableFuture<V>)getRunnable()).setException(t);
182 }
183 }
184
185
186 /**
187 * An annoying wrapper class to convince generics compiler to
188 * use a DelayQueue<ScheduledCancellableTask> as a BlockingQueue<Runnable>
189 */
190 private static class DelayedWorkQueue
191 extends AbstractCollection<Runnable> implements BlockingQueue<Runnable> {
192
193 private final DelayQueue<ScheduledCancellableTask> dq = new DelayQueue<ScheduledCancellableTask>();
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((ScheduledCancellableTask)x); }
202 public boolean offer(Runnable x) { return dq.offer((ScheduledCancellableTask)x); }
203 public void put(Runnable x) throws InterruptedException {
204 dq.put((ScheduledCancellableTask)x);
205 }
206 public boolean offer(Runnable x, long timeout, TimeUnit unit) throws InterruptedException {
207 return dq.offer((ScheduledCancellableTask)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<ScheduledCancellableTask> 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 ScheduledCancellable schedule(Runnable command, long delay, TimeUnit unit) {
314 long triggerTime = System.nanoTime() + unit.toNanos(delay);
315 ScheduledCancellableTask t = new ScheduledCancellableTask(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 ScheduledCancellable schedule(Runnable command, Date date) {
330 long triggerTime = System.nanoTime() +
331 TimeUnit.MILLISECONDS.toNanos(date.getTime() -
332 System.currentTimeMillis());
333 ScheduledCancellableTask t = new ScheduledCancellableTask(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 ScheduledCancellable scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
353 long triggerTime = System.nanoTime() + unit.toNanos(initialDelay);
354 ScheduledCancellableTask t = new ScheduledCancellableTask(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 ScheduledCancellable scheduleAtFixedRate(Runnable command, Date initialDate, long period, TimeUnit unit) {
378 long triggerTime = System.nanoTime() +
379 TimeUnit.MILLISECONDS.toNanos(initialDate.getTime() -
380 System.currentTimeMillis());
381 ScheduledCancellableTask t = new ScheduledCancellableTask(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 ScheduledCancellable scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
404 long triggerTime = System.nanoTime() + unit.toNanos(initialDelay);
405 ScheduledCancellableTask t = new ScheduledCancellableTask(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 ScheduledCancellable scheduleWithFixedDelay(Runnable command, Date initialDate, long delay, TimeUnit unit) {
428 long triggerTime = System.nanoTime() +
429 TimeUnit.MILLISECONDS.toNanos(initialDate.getTime() -
430 System.currentTimeMillis());
431 ScheduledCancellableTask t = new ScheduledCancellableTask(command,
432 triggerTime,
433 unit.toNanos(delay),
434 false);
435 delayedExecute(t);
436 return t;
437 }
438
439
440 /**
441 * Creates and executes a ScheduledFuture 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 ScheduledFuture 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> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
451 long triggerTime = System.nanoTime() + unit.toNanos(delay);
452 ScheduledFutureTask<V> t = new ScheduledFutureTask<V>(callable, triggerTime);
453 delayedExecute(t);
454 return t;
455 }
456
457 /**
458 * Creates and executes a one-shot action that becomes enabled after
459 * the given date.
460 * @param callable the function to execute.
461 * @param date the time to commence excution.
462 * @return a ScheduledFuture that can be used to extract result or cancel.
463 * @throws RejectedExecutionException if task cannot be scheduled
464 * for execution because the executor has been shut down.
465 */
466 public <V> ScheduledFuture<V> schedule(Callable<V> callable, Date date) {
467 long triggerTime = System.nanoTime() +
468 TimeUnit.MILLISECONDS.toNanos(date.getTime() -
469 System.currentTimeMillis());
470 ScheduledFutureTask<V> t = new ScheduledFutureTask<V>(callable, triggerTime);
471 delayedExecute(t);
472 return t;
473 }
474
475 /**
476 * Execute command with zero required delay. This has effect
477 * equivalent to <tt>schedule(command, 0, anyUnit)</tt>. Note
478 * that inspections of the queue and of the list returned by
479 * <tt>shutdownNow</tt> will access the zero-delayed
480 * <tt>ScheduledCancellable</tt>, not the <tt>command</tt> itself.
481 *
482 * @param command the task to execute
483 * @throws RejectedExecutionException at discretion of
484 * <tt>RejectedExecutionHandler</tt>, if task cannot be accepted
485 * for execution because the executor has been shut down.
486 */
487 public void execute(Runnable command) {
488 schedule(command, 0, TimeUnit.NANOSECONDS);
489 }
490
491
492 /**
493 * Set policy on whether to continue executing existing periodic
494 * tasks even when this executor has been <tt>shutdown</tt>. In
495 * this case, these tasks will only terminate upon
496 * <tt>shutdownNow</tt>, or after setting the policy to
497 * <tt>false</tt> when already shutdown. This value is by default
498 * false.
499 * @param value if true, continue after shutdown, else don't.
500 */
501 public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean value) {
502 continueExistingPeriodicTasksAfterShutdown = value;
503 if (!value && isShutdown())
504 cancelUnwantedTasks();
505 }
506
507 /**
508 * Get the policy on whether to continue executing existing
509 * periodic tasks even when this executor has been
510 * <tt>shutdown</tt>. In this case, these tasks will only
511 * terminate upon <tt>shutdownNow</tt> or after setting the policy
512 * to <tt>false</tt> when already shutdown. This value is by
513 * default false.
514 * @return true if will continue after shutdown.
515 */
516 public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy() {
517 return continueExistingPeriodicTasksAfterShutdown;
518 }
519
520 /**
521 * Set policy on whether to execute existing delayed
522 * tasks even when this executor has been <tt>shutdown</tt>. In
523 * this case, these tasks will only terminate upon
524 * <tt>shutdownNow</tt>, or after setting the policy to
525 * <tt>false</tt> when already shutdown. This value is by default
526 * true.
527 * @param value if true, execute after shutdown, else don't.
528 */
529 public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean value) {
530 executeExistingDelayedTasksAfterShutdown = value;
531 if (!value && isShutdown())
532 cancelUnwantedTasks();
533 }
534
535 /**
536 * Set policy on whether to execute existing delayed
537 * tasks even when this executor has been <tt>shutdown</tt>. In
538 * this case, these tasks will only terminate upon
539 * <tt>shutdownNow</tt>, or after setting the policy to
540 * <tt>false</tt> when already shutdown. This value is by default
541 * true.
542 * @return true if will execute after shutdown.
543 */
544 public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy() {
545 return executeExistingDelayedTasksAfterShutdown;
546 }
547
548 /**
549 * Cancel and clear the queue of all tasks that should not be run
550 * due to shutdown policy.
551 */
552 private void cancelUnwantedTasks() {
553 boolean keepDelayed = getExecuteExistingDelayedTasksAfterShutdownPolicy();
554 boolean keepPeriodic = getContinueExistingPeriodicTasksAfterShutdownPolicy();
555 if (!keepDelayed && !keepPeriodic)
556 super.getQueue().clear();
557 else if (keepDelayed || keepPeriodic) {
558 Object[] entries = super.getQueue().toArray();
559 for (int i = 0; i < entries.length; ++i) {
560 ScheduledCancellableTask t = (ScheduledCancellableTask)entries[i];
561 if (t.isPeriodic()? !keepPeriodic : !keepDelayed)
562 t.cancel(false);
563 }
564 entries = null;
565 purge();
566 }
567 }
568
569 /**
570 * Initiates an orderly shutdown in which previously submitted
571 * tasks are executed, but no new tasks will be accepted. If the
572 * <tt>ExecuteExistingDelayedTasksAfterShutdownPolicy</tt> has
573 * been set <tt>false</tt>, existing delayed tasks whose delays
574 * have not yet elapsed are cancelled. And unless the
575 * <tt>ContinueExistingPeriodicTasksAfterShutdownPolicy</tt> hase
576 * been set <tt>true</tt>, future executions of existing periodic
577 * tasks will be cancelled.
578 */
579 public void shutdown() {
580 cancelUnwantedTasks();
581 super.shutdown();
582 }
583
584 /**
585 * Attempts to stop all actively executing tasks, halts the
586 * processing of waiting tasks, and returns a list of the tasks that were
587 * awaiting execution.
588 *
589 * <p>There are no guarantees beyond best-effort attempts to stop
590 * processing actively executing tasks. This implementations
591 * cancels via {@link Thread#interrupt}, so if any tasks mask or
592 * fail to respond to interrupts, they may never terminate.
593 *
594 * @return list of tasks that never commenced execution. Each
595 * element of this list is a <tt>ScheduledCancellable</tt>, including those
596 * tasks submitted using <tt>execute</tt> which are for scheduling
597 * purposes used as the basis of a zero-delay <tt>ScheduledCancellable</tt>.
598 */
599 public List shutdownNow() {
600 return super.shutdownNow();
601 }
602
603 /**
604 * Removes this task from internal queue if it is present, thus
605 * causing it not to be run if it has not already started. This
606 * method may be useful as one part of a cancellation scheme.
607 *
608 * @param task the task to remove
609 * @return true if the task was removed
610 */
611 public boolean remove(Runnable task) {
612 if (task instanceof ScheduledCancellable && super.getQueue().remove(task))
613 return true;
614
615 // The task might actually have been wrapped as a ScheduledCancellable
616 // in execute(), in which case we need to maually traverse
617 // looking for it.
618
619 ScheduledCancellable wrap = null;
620 Object[] entries = super.getQueue().toArray();
621 for (int i = 0; i < entries.length; ++i) {
622 ScheduledCancellableTask t = (ScheduledCancellableTask)entries[i];
623 Runnable r = t.getRunnable();
624 if (task.equals(r)) {
625 wrap = t;
626 break;
627 }
628 }
629 entries = null;
630 return wrap != null && super.getQueue().remove(wrap);
631 }
632
633
634 /**
635 * Returns the task queue used by this executor. Each element
636 * of this queue is a <tt>ScheduledCancellable</tt>, including those
637 * tasks submitted using <tt>execute</tt> which are for
638 * scheduling purposes used as the basis of a zero-delay
639 * <tt>ScheduledCancellable</tt>.
640 *
641 * @return the task queue
642 */
643 public BlockingQueue<Runnable> getQueue() {
644 return super.getQueue();
645 }
646
647 /**
648 * If executed task was periodic, cause the task for the next
649 * period to execute.
650 * @param r the task (assumed to be a ScheduledCancellable)
651 * @param t the exception
652 */
653 protected void afterExecute(Runnable r, Throwable t) {
654 super.afterExecute(r, t);
655 ScheduledCancellableTask next = ((ScheduledCancellableTask)r).nextTask();
656 if (next != null &&
657 (!isShutdown() ||
658 (getContinueExistingPeriodicTasksAfterShutdownPolicy() &&
659 !isTerminating())))
660 super.getQueue().offer(next);
661
662 // This might have been the final executed delayed task. Wake
663 // up threads to check.
664 else if (isShutdown())
665 interruptIdleWorkers();
666 }
667 }