ExecutorService that interrupts tasks after a timeout

后端 未结 9 2191
隐瞒了意图╮
隐瞒了意图╮ 2020-11-22 16:19

I\'m looking for an ExecutorService implementation that can be provided with a timeout. Tasks that are submitted to the ExecutorService are interrupted if they take longer t

相关标签:
9条回答
  • 2020-11-22 16:39

    After ton of time to survey,
    Finally, I use invokeAll method of ExecutorService to solve this problem.
    That will strictly interrupt the task while task running.
    Here is example

    ExecutorService executorService = Executors.newCachedThreadPool();
    
    try {
        List<Callable<Object>> callables = new ArrayList<>();
        // Add your long time task (callable)
        callables.add(new VaryLongTimeTask());
        // Assign tasks for specific execution timeout (e.g. 2 sec)
        List<Future<Object>> futures = executorService.invokeAll(callables, 2000, TimeUnit.MILLISECONDS);
        for (Future<Object> future : futures) {
            // Getting result
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    
    executorService.shutdown();
    

    The pro is you can also submit ListenableFuture at the same ExecutorService.
    Just slightly change the first line of code.

    ListeningExecutorService executorService = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool());
    

    ListeningExecutorService is the Listening feature of ExecutorService at google guava project (com.google.guava) )

    0 讨论(0)
  • 2020-11-22 16:40

    You can use a ScheduledExecutorService for this. First you would submit it only once to begin immediately and retain the future that is created. After that you can submit a new task that would cancel the retained future after some period of time.

     ScheduledExecutorService executor = Executors.newScheduledThreadPool(2); 
     final Future handler = executor.submit(new Callable(){ ... });
     executor.schedule(new Runnable(){
         public void run(){
             handler.cancel();
         }      
     }, 10000, TimeUnit.MILLISECONDS);
    

    This will execute your handler (main functionality to be interrupted) for 10 seconds, then will cancel (i.e. interrupt) that specific task.

    0 讨论(0)
  • 2020-11-22 16:40

    How about using the ExecutorService.shutDownNow() method as described in http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html? It seems to be the simplest solution.

    0 讨论(0)
  • 2020-11-22 16:41

    Wrap the task in FutureTask and you can specify timeout for the FutureTask. Look at the example in my answer to this question,

    java native Process timeout

    0 讨论(0)
  • 2020-11-22 16:43

    What about this alternative idea :

    • two have two executors :
      • one for :
        • submitting the task, without caring about the timeout of the task
        • adding the Future resulted and the time when it should end to an internal structure
      • one for executing an internal job which is checking the internal structure if some tasks are timeout and if they have to be cancelled.

    Small sample is here :

    public class AlternativeExecutorService 
    {
    
    private final CopyOnWriteArrayList<ListenableFutureTask> futureQueue       = new CopyOnWriteArrayList();
    private final ScheduledThreadPoolExecutor                scheduledExecutor = new ScheduledThreadPoolExecutor(1); // used for internal cleaning job
    private final ListeningExecutorService                   threadExecutor    = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(5)); // used for
    private ScheduledFuture scheduledFuture;
    private static final long INTERNAL_JOB_CLEANUP_FREQUENCY = 1000L;
    
    public AlternativeExecutorService()
    {
        scheduledFuture = scheduledExecutor.scheduleAtFixedRate(new TimeoutManagerJob(), 0, INTERNAL_JOB_CLEANUP_FREQUENCY, TimeUnit.MILLISECONDS);
    }
    
    public void pushTask(OwnTask task)
    {
        ListenableFuture<Void> future = threadExecutor.submit(task);  // -> create your Callable
        futureQueue.add(new ListenableFutureTask(future, task, getCurrentMillisecondsTime())); // -> store the time when the task should end
    }
    
    public void shutdownInternalScheduledExecutor()
    {
        scheduledFuture.cancel(true);
        scheduledExecutor.shutdownNow();
    }
    
    long getCurrentMillisecondsTime()
    {
        return Calendar.getInstance().get(Calendar.MILLISECOND);
    }
    
    class ListenableFutureTask
    {
        private final ListenableFuture<Void> future;
        private final OwnTask                task;
        private final long                   milliSecEndTime;
    
        private ListenableFutureTask(ListenableFuture<Void> future, OwnTask task, long milliSecStartTime)
        {
            this.future = future;
            this.task = task;
            this.milliSecEndTime = milliSecStartTime + task.getTimeUnit().convert(task.getTimeoutDuration(), TimeUnit.MILLISECONDS);
        }
    
        ListenableFuture<Void> getFuture()
        {
            return future;
        }
    
        OwnTask getTask()
        {
            return task;
        }
    
        long getMilliSecEndTime()
        {
            return milliSecEndTime;
        }
    }
    
    class TimeoutManagerJob implements Runnable
    {
        CopyOnWriteArrayList<ListenableFutureTask> getCopyOnWriteArrayList()
        {
            return futureQueue;
        }
    
        @Override
        public void run()
        {
            long currentMileSecValue = getCurrentMillisecondsTime();
            for (ListenableFutureTask futureTask : futureQueue)
            {
                consumeFuture(futureTask, currentMileSecValue);
            }
        }
    
        private void consumeFuture(ListenableFutureTask futureTask, long currentMileSecValue)
        {
            ListenableFuture<Void> future = futureTask.getFuture();
            boolean isTimeout = futureTask.getMilliSecEndTime() >= currentMileSecValue;
            if (isTimeout)
            {
                if (!future.isDone())
                {
                    future.cancel(true);
                }
                futureQueue.remove(futureTask);
            }
        }
    }
    
    class OwnTask implements Callable<Void>
    {
        private long     timeoutDuration;
        private TimeUnit timeUnit;
    
        OwnTask(long timeoutDuration, TimeUnit timeUnit)
        {
            this.timeoutDuration = timeoutDuration;
            this.timeUnit = timeUnit;
        }
    
        @Override
        public Void call() throws Exception
        {
            // do logic
            return null;
        }
    
        public long getTimeoutDuration()
        {
            return timeoutDuration;
        }
    
        public TimeUnit getTimeUnit()
        {
            return timeUnit;
        }
    }
    }
    
    0 讨论(0)
  • 2020-11-22 16:46

    Using John W answer I created an implementation that correctly begin the timeout when the task starts its execution. I even write a unit test for it :)

    However, it does not suit my needs since some IO operations do not interrupt when Future.cancel() is called (ie when Thread.interrupt() is called). Some examples of IO operation that may not be interrupted when Thread.interrupt() is called are Socket.connect and Socket.read (and I suspect most of IO operation implemented in java.io). All IO operations in java.nio should be interruptible when Thread.interrupt() is called. For example, that is the case for SocketChannel.open and SocketChannel.read.

    Anyway if anyone is interested, I created a gist for a thread pool executor that allows tasks to timeout (if they are using interruptible operations...): https://gist.github.com/amanteaux/64c54a913c1ae34ad7b86db109cbc0bf

    0 讨论(0)
提交回复
热议问题