Как запустить невыполненные задачи сразу после ExecutorService.shutdown()?
у меня есть ScheduledExecutorService
с задачами, запланированными для выполнения через час. Как получить список невыполненных задач, чтобы заставить их работать немедленно?
Я считаю shutdown()
будет ждать час, и похоже, что shutdownNow()
возвращает список Runnables, которые не могут быть запущены (), потому что Runnable реализация проверяет состояние исполнителя и когда он замечает, что он закрыл Runnable отказывается работать. См.ScheduledThreadPoolExecutor.ScheduledFutureTask.run()
для фактической реализации.
любой идеи?
2 ответов
Я принял ответ Марка Питерса, реализуя все абстрактные методы, добавил потокобезопасность и попытался соблюдать базовую конфигурацию ScheduledThreadPoolExecutor, когда это возможно.
/**
* Overrides shutdown() to run outstanding tasks immediately.
*
* @author Gili Tzabari
*/
public class RunOnShutdownScheduledExecutorService extends AbstractExecutorService
implements ScheduledExecutorService
{
private final ScheduledExecutorService delegate;
private final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor;
private final ExecutorService immediateService;
private final ConcurrentMap<Future<?>, Callable<?>> tasks = Maps.newConcurrentMap();
/**
* Creates a new RunOnShutdownScheduledExecutorService.
*
* @param delegate the executor to delegate to
*/
public RunOnShutdownScheduledExecutorService(ScheduledExecutorService delegate)
{
Preconditions.checkNotNull(delegate, "delegate may not be null");
this.delegate = delegate;
if (delegate instanceof ScheduledThreadPoolExecutor)
{
this.scheduledThreadPoolExecutor = (ScheduledThreadPoolExecutor) delegate;
this.immediateService = Executors.newFixedThreadPool(scheduledThreadPoolExecutor.
getCorePoolSize(), scheduledThreadPoolExecutor.getThreadFactory());
}
else
{
scheduledThreadPoolExecutor = null;
this.immediateService = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().
setNameFormat(RunOnShutdownScheduledExecutorService.class.getName() + "-%d").build());
}
}
@Override
public boolean isShutdown()
{
return delegate.isShutdown();
}
@Override
public boolean isTerminated()
{
return delegate.isTerminated();
}
@Override
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException
{
long before = System.nanoTime();
if (!delegate.awaitTermination(timeout, unit))
return false;
long after = System.nanoTime();
long timeLeft = timeout - unit.convert(after - before, TimeUnit.NANOSECONDS);
return immediateService.awaitTermination(timeLeft, unit);
}
@Override
public void execute(Runnable command)
{
delegate.execute(command);
}
@Override
public ScheduledFuture<?> schedule(final Runnable command, long delay, TimeUnit unit)
{
CleaningRunnable decorated = new CleaningRunnable(command);
ScheduledFuture<?> future = delegate.schedule(decorated, delay, unit);
decorated.setFuture(future);
tasks.put(future, Executors.callable(command));
return new CleaningScheduledFuture<>(future);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
{
CallableWithFuture<V> decorated = new CallableWithFuture<>(callable);
ScheduledFuture<V> future = delegate.schedule(decorated, delay, unit);
decorated.setFuture(future);
tasks.put(future, callable);
return new CleaningScheduledFuture<>(future);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
TimeUnit unit)
{
CleaningRunnable decorated = new CleaningRunnable(command);
ScheduledFuture<?> future = delegate.scheduleAtFixedRate(decorated, initialDelay, period, unit);
decorated.setFuture(future);
tasks.put(future, Executors.callable(command));
return new CleaningScheduledFuture<>(future);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,
TimeUnit unit)
{
CleaningRunnable decorated = new CleaningRunnable(command);
ScheduledFuture<?> future =
delegate.scheduleWithFixedDelay(decorated, initialDelay, delay, unit);
decorated.setFuture(future);
tasks.put(future, Executors.callable(command));
return new CleaningScheduledFuture<>(future);
}
@Override
public synchronized void shutdown()
{
if (delegate.isShutdown())
return;
if (scheduledThreadPoolExecutor != null)
{
// WORKAROUND: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7069418
//
// Cancel waiting scheduled tasks, otherwise executor won't shut down
scheduledThreadPoolExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
}
delegate.shutdown();
// Users will not be able to cancel() Futures past this point so we're guaranteed that
// "tasks" will not be modified.
final List<Callable<?>> outstandingTasks = Lists.newArrayList();
for (Map.Entry<Future<?>, Callable<?>> entry: tasks.entrySet())
{
Future<?> future = entry.getKey();
Callable<?> task = entry.getValue();
if (future.isDone() && future.isCancelled())
{
// Task called by the underlying executor, not the user. See CleaningScheduledFuture.
outstandingTasks.add(task);
}
}
tasks.clear();
if (outstandingTasks.isEmpty())
{
immediateService.shutdown();
return;
}
immediateService.submit(new Callable<Void>()
{
@Override
public Void call() throws Exception
{
delegate.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
// Execute outstanding tasks only after the delegate executor finishes shutting down
for (Callable<?> task: outstandingTasks)
immediateService.submit(task);
immediateService.shutdown();
return null;
}
});
}
@Override
public List<Runnable> shutdownNow()
{
return delegate.shutdownNow();
}
/**
* A Runnable that removes its future when running.
*/
private class CleaningRunnable implements Runnable
{
private final Runnable delegate;
private Future<?> future;
/**
* Creates a new RunnableWithFuture.
*
* @param delegate the Runnable to delegate to
* @throws NullPointerException if delegate is null
*/
public CleaningRunnable(Runnable delegate)
{
Preconditions.checkNotNull(delegate, "delegate may not be null");
this.delegate = delegate;
}
/**
* Associates a Future with the runnable.
*
* @param future a future
*/
public void setFuture(Future<?> future)
{
this.future = future;
}
@Override
public void run()
{
tasks.remove(future);
delegate.run();
}
}
/**
* A Callable that removes its future when running.
*/
private class CallableWithFuture<V> implements Callable<V>
{
private final Callable<V> delegate;
private Future<V> future;
/**
* Creates a new CallableWithFuture.
*
* @param delegate the Callable to delegate to
* @throws NullPointerException if delegate is null
*/
public CallableWithFuture(Callable<V> delegate)
{
Preconditions.checkNotNull(delegate, "delegate may not be null");
this.delegate = delegate;
}
/**
* Associates a Future with the runnable.
*
* @param future a future
*/
public void setFuture(Future<V> future)
{
this.future = future;
}
@Override
public V call() throws Exception
{
tasks.remove(future);
return delegate.call();
}
}
/**
* A ScheduledFuture that removes its future when canceling.
*
* This allows us to differentiate between tasks canceled by the user and the underlying
* executor. Tasks canceled by the user are removed from "tasks".
*
* @param <V> The result type returned by this Future
*/
private class CleaningScheduledFuture<V> implements ScheduledFuture<V>
{
private final ScheduledFuture<V> delegate;
/**
* Creates a new MyScheduledFuture.
*
* @param delegate the future to delegate to
* @throws NullPointerException if delegate is null
*/
public CleaningScheduledFuture(ScheduledFuture<V> delegate)
{
Preconditions.checkNotNull(delegate, "delegate may not be null");
this.delegate = delegate;
}
@Override
public long getDelay(TimeUnit unit)
{
return delegate.getDelay(unit);
}
@Override
public int compareTo(Delayed o)
{
return delegate.compareTo(o);
}
@Override
public boolean cancel(boolean mayInterruptIfRunning)
{
boolean result = delegate.cancel(mayInterruptIfRunning);
if (result)
{
// Tasks canceled by users are removed from "tasks"
tasks.remove(delegate);
}
return result;
}
@Override
public boolean isCancelled()
{
return delegate.isCancelled();
}
@Override
public boolean isDone()
{
return delegate.isDone();
}
@Override
public V get() throws InterruptedException, ExecutionException
{
return delegate.get();
}
@Override
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException,
TimeoutException
{
return delegate.get(timeout, unit);
}
}
}
большой вопрос! Похоже, что вы можете самостоятельно латать вместе решение.
одним из вариантов может быть, чтобы обернуть ScheduledThreadPoolExecutor
С вашей собственной реализации ScheduledExecutorService
. Когда придет время завершить работу службы, отмените все задачи, которые могут быть отменены, и отправьте их службе, которая выполнит их немедленно. Тогда shutdown()
эту услугу.
вот очень грубый код, который демонстрирует, что я имею в виду, хотя я предупреждаю вас, что может быть ловушки здесь, так как он был взбитый в течение нескольких минут. В частности, я не приложил много усилий, чтобы гарантировать, что это безопасно.
class RunOnShutdownScheduledExecutorService extends AbstractExecutorService implements ScheduledExecutorService {
private final ScheduledExecutorService delegateService;
private Map<Future<?>, Runnable> scheduledFutures =
Collections.synchronizedMap(new IdentityHashMap<Future<?>, Runnable>());
public RunOnShutdownScheduledExecutorService(ScheduledExecutorService delegateService) {
this.delegateService = delegateService;
}
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
ScheduledFuture<?> future = delegateService.schedule(command, delay, unit);
scheduledFutures.put(future, command);
return future;
}
public void shutdown() {
delegateService.shutdown();
ExecutorService immediateService = Executors.newFixedThreadPool(5);
for (Map.Entry<Future<?>, Runnable> entry : scheduledFutures.entrySet()) {
Future<?> future = entry.getKey();
Runnable task = entry.getValue();
if (!future.isDone()) {
if (future.cancel(false)) {
immediateService.submit(task);
}
}
}
immediateService.shutdown();
}
//...
}