ExecutorService with Timeout for Interruptable Tasks
When executing tasks concurrently, it's often desirable to have a mechanism to gracefully interrupt tasks that exceed a specified timeout. This is particularly useful in situations where long-running tasks can lead to application lockups or performance issues.
Implementing a TimeoutExecutorService
Here's an implementation of a TimeoutThreadPoolExecutor that extends the standard ThreadPoolExecutor and incorporates a timeout feature:
class TimeoutThreadPoolExecutor extends ThreadPoolExecutor { private final long timeout; private final TimeUnit timeoutUnit; private final ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor(); private final ConcurrentMap<Runnable, ScheduledFuture> runningTasks = new ConcurrentHashMap<>(); public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, long timeout, TimeUnit timeoutUnit) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); this.timeout = timeout; this.timeoutUnit = timeoutUnit; } // Override methods to implement timeout behavior @Override protected void beforeExecute(Thread t, Runnable r) { if (timeout > 0) { ScheduledFuture scheduled = timeoutExecutor.schedule(new TimeoutTask(t), timeout, timeoutUnit); runningTasks.put(r, scheduled); } } @Override protected void afterExecute(Runnable r, Throwable t) { ScheduledFuture timeoutTask = runningTasks.remove(r); if (timeoutTask != null) { timeoutTask.cancel(false); } } // Timeout task to interrupt threads class TimeoutTask implements Runnable { private final Thread thread; public TimeoutTask(Thread thread) { this.thread = thread; } @Override public void run() { thread.interrupt(); } } }
Usage:
To use the TimeoutThreadPoolExecutor, you can simply instantiate it with a timeout value:
TimeoutThreadPoolExecutor executor = new TimeoutThreadPoolExecutor( 4, // corePoolSize 8, // maximumPoolSize 1, // keepAliveTime TimeUnit.SECONDS, // timeUnit new LinkedBlockingQueue<>(), // workQueue 5, // timeout TimeUnit.SECONDS // timeoutUnit );
Then, submit tasks to the executor as usual:
executor.submit(() -> { // long-running task });
If a task takes longer than the specified timeout, the thread executing the task will be interrupted, causing the task to terminate gracefully.
Alternative Solution
Another approach to implementing a timeout for a task is to use the ScheduledExecutorService as suggested in the response. This involves submitting the task as a Callable and retaining the created future. A second task can then be scheduled to cancel the future after a certain period, effectively interrupting the task.
The above is the detailed content of How Can I Implement a Timeout for Interruptable Tasks in an ExecutorService?. For more information, please follow other related articles on the PHP Chinese website!