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 |
|
*/ |
109 |
|
} |
110 |
|
|
111 |
|
public int compareTo(Object other) { |
112 |
< |
if (other == this) |
112 |
> |
if (other == this) // compare zero ONLY if same object |
113 |
|
return 0; |
114 |
|
DelayedTask x = (DelayedTask)other; |
115 |
|
long diff = time - x.time; |
151 |
|
long nextTime = period + (rateBased ? time : System.nanoTime()); |
152 |
|
return new DelayedTask(getRunnable(), nextTime, period, rateBased); |
153 |
|
} |
143 |
– |
|
154 |
|
} |
155 |
|
|
156 |
|
/** |
285 |
|
/** |
286 |
|
* Specialized variant of ThreadPoolExecutor.execute for delayed tasks. |
287 |
|
*/ |
288 |
< |
void delayedExecute(Runnable command) { |
288 |
> |
private void delayedExecute(Runnable command) { |
289 |
|
if (isShutdown()) { |
290 |
|
reject(command); |
291 |
|
return; |
292 |
|
} |
293 |
< |
// Prestart thread if necessary. We cannot prestart it running |
294 |
< |
// the task because the task (probably) shouldn't be run yet, |
295 |
< |
// so thread will just idle until delay elapses. |
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 |
< |
addIfUnderCorePoolSize(null); |
297 |
> |
prestartCoreThread(); |
298 |
|
|
299 |
|
getQueue().offer(command); |
300 |
|
} |
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 |
+ |
getQueue().clear(); |
559 |
+ |
else if (keepDelayed || keepPeriodic) { |
560 |
+ |
Object[] entries = 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 |
|
* Removes this task from internal queue if it is present, thus |
588 |
|
* causing it not to be run if it has not already started. This |
620 |
|
* @param t the exception |
621 |
|
*/ |
622 |
|
protected void afterExecute(Runnable r, Throwable t) { |
520 |
– |
if (isShutdown()) |
521 |
– |
return; |
623 |
|
super.afterExecute(r, t); |
624 |
< |
DelayedTask d = (DelayedTask)r; |
625 |
< |
DelayedTask next = d.nextTask(); |
626 |
< |
if (next == null) |
627 |
< |
return; |
628 |
< |
try { |
629 |
< |
delayedExecute(next); |
630 |
< |
} catch(RejectedExecutionException ex) { |
631 |
< |
// lost race to detect shutdown; ignore |
632 |
< |
} |
624 |
> |
DelayedTask next = ((DelayedTask)r).nextTask(); |
625 |
> |
if (next != null && |
626 |
> |
(!isShutdown() || |
627 |
> |
(getContinueExistingPeriodicTasksAfterShutdownPolicy() && |
628 |
> |
!isTerminating()))) |
629 |
> |
getQueue().offer(next); |
630 |
> |
|
631 |
> |
// This might have been the final executed delayed task. Wake |
632 |
> |
// up threads to check. |
633 |
> |
else if (isShutdown()) |
634 |
> |
interruptIdleWorkers(); |
635 |
|
} |
636 |
|
} |
534 |
– |
|
535 |
– |
|