ExecutorService that interrupts tasks after a timeout

后端 未结 9 2192
隐瞒了意图╮
隐瞒了意图╮ 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:47

    Unfortunately the solution is flawed. There is a sort of bug with ScheduledThreadPoolExecutor, also reported in this question: cancelling a submitted task does not fully release the memory resources associated with the task; the resources are released only when the task expires.

    If you therefore create a TimeoutThreadPoolExecutor with a fairly long expiration time (a typical usage), and submit tasks fast enough, you end up filling the memory - even though the tasks actually completed successfully.

    You can see the problem with the following (very crude) test program:

    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = new TimeoutThreadPoolExecutor(1, 1, 10, TimeUnit.SECONDS, 
                new LinkedBlockingQueue<Runnable>(), 10, TimeUnit.MINUTES);
        //ExecutorService service = Executors.newFixedThreadPool(1);
        try {
            final AtomicInteger counter = new AtomicInteger();
            for (long i = 0; i < 10000000; i++) {
                service.submit(new Runnable() {
                    @Override
                    public void run() {
                        counter.incrementAndGet();
                    }
                });
                if (i % 10000 == 0) {
                    System.out.println(i + "/" + counter.get());
                    while (i > counter.get()) {
                        Thread.sleep(10);
                    }
                }
            }
        } finally {
            service.shutdown();
        }
    }
    

    The program exhausts the available memory, although it waits for the spawned Runnables to complete.

    I though about this for a while, but unfortunately I could not come up with a good solution.

    EDIT: I found out this issue was reported as JDK bug 6602600, and appears to have been fixed very recently.

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

    It seems problem is not in JDK bug 6602600 ( it was solved at 2010-05-22), but in incorrect call of sleep(10) in circle. Addition note, that the main Thread must give directly CHANCE to other threads to realize thier tasks by invoke SLEEP(0) in EVERY branch of outer circle. It is better, I think, to use Thread.yield() instead of Thread.sleep(0)

    The result corrected part of previous problem code is such like this:

    .......................
    ........................
    Thread.yield();         
    
    if (i % 1000== 0) {
    System.out.println(i + "/" + counter.get()+ "/"+service.toString());
    }
    
    //                
    //                while (i > counter.get()) {
    //                    Thread.sleep(10);
    //                } 
    

    It works correctly with amount of outer counter up to 150 000 000 tested circles.

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

    check if this works for you,

        public <T,S,K,V> ResponseObject<Collection<ResponseObject<T>>> runOnScheduler(ThreadPoolExecutor threadPoolExecutor,
          int parallelismLevel, TimeUnit timeUnit, int timeToCompleteEachTask, Collection<S> collection,
          Map<K,V> context, Task<T,S,K,V> someTask){
        if(threadPoolExecutor==null){
          return ResponseObject.<Collection<ResponseObject<T>>>builder().errorCode("500").errorMessage("threadPoolExecutor can not be null").build();
        }
        if(someTask==null){
          return ResponseObject.<Collection<ResponseObject<T>>>builder().errorCode("500").errorMessage("Task can not be null").build();
        }
        if(CollectionUtils.isEmpty(collection)){
          return ResponseObject.<Collection<ResponseObject<T>>>builder().errorCode("500").errorMessage("input collection can not be empty").build();
        }
    
        LinkedBlockingQueue<Callable<T>> callableLinkedBlockingQueue = new LinkedBlockingQueue<>(collection.size());
        collection.forEach(value -> {
          callableLinkedBlockingQueue.offer(()->someTask.perform(value,context)); //pass some values in callable. which can be anything.
        });
        LinkedBlockingQueue<Future<T>> futures = new LinkedBlockingQueue<>();
    
        int count = 0;
    
        while(count<parallelismLevel && count < callableLinkedBlockingQueue.size()){
          Future<T> f = threadPoolExecutor.submit(callableLinkedBlockingQueue.poll());
          futures.offer(f);
          count++;
        }
    
        Collection<ResponseObject<T>> responseCollection = new ArrayList<>();
    
        while(futures.size()>0){
          Future<T> future = futures.poll();
          ResponseObject<T> responseObject = null;
            try {
              T response = future.get(timeToCompleteEachTask, timeUnit);
              responseObject = ResponseObject.<T>builder().data(response).build();
            } catch (InterruptedException e) {
              future.cancel(true);
            } catch (ExecutionException e) {
              future.cancel(true);
            } catch (TimeoutException e) {
              future.cancel(true);
            } finally {
              if (Objects.nonNull(responseObject)) {
                responseCollection.add(responseObject);
              }
              futures.remove(future);//remove this
              Callable<T> callable = getRemainingCallables(callableLinkedBlockingQueue);
              if(null!=callable){
                Future<T> f = threadPoolExecutor.submit(callable);
                futures.add(f);
              }
            }
    
        }
        return ResponseObject.<Collection<ResponseObject<T>>>builder().data(responseCollection).build();
      }
    
      private <T> Callable<T> getRemainingCallables(LinkedBlockingQueue<Callable<T>> callableLinkedBlockingQueue){
        if(callableLinkedBlockingQueue.size()>0){
          return callableLinkedBlockingQueue.poll();
        }
        return null;
      }
    

    you can restrict the no of thread uses from scheduler as well as put timeout on the task.

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