简体   繁体   中英

Threadpool task executor timeout for working thread

I am using spring boot and have one async method. To execute async I have below configuration, questions is what if all those 5 thread hangs for some reason , essentially it will lock the application and none of new task will be executed (it will just keep accepting). How we can set timeout for those working thread , lets say 120 seconds, so after that it timesout and execute new task. (Yes I am using fixed thread pool with unbounded queue to keep accepting tasks)

@EnableAsync
@Configuration
public class AsyncConfiguration implements AsyncConfigurer {

@Override
public Executor getAsyncExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(5);
    taskExecutor.setMaxPoolSize(5);
    taskExecutor.initialize();
    return taskExecutor;
}

@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
    return new SimpleAsyncUncaughtExceptionHandler();
}

}

You can create another executor like:

static class TimeOutExecutorService extends CompletableExecutors.DelegatingCompletableExecutorService {
    private final Duration timeout;
    private final ScheduledExecutorService schedulerExecutor;

    TimeOutExecutorService(ExecutorService delegate, Duration timeout) {
        super(delegate);
        this.timeout = timeout;
        schedulerExecutor = Executors.newScheduledThreadPool(1);
    }

    @Override public <T> CompletableFuture<T> submit(Callable<T> task) {
        CompletableFuture<T> cf = new CompletableFuture<>();
        Future<?> future = delegate.submit(() -> {
            try {
                cf.complete(task.call());
            } catch (CancellationException e) {
                cf.cancel(true);
            } catch (Throwable ex) {
                cf.completeExceptionally(ex);
            }
        });

        schedulerExecutor.schedule(() -> {
            if (!cf.isDone()) {
                cf.completeExceptionally(new TimeoutException("Timeout after " + timeout));
                future.cancel(true);
            }
        }, timeout.toMillis(), TimeUnit.MILLISECONDS);
        return cf;
    }
}

Then, create a new bean named timed

@Bean(name = "timed")
public Executor timeoutExecutor() {
    ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("timed-%d").build();
    return TimedCompletables.timed(Executors.newFixedThreadPool(10, threadFactory), Duration.ofSeconds(2));
}

And, try to use this Executor to execute your async tasks.

Or, try to change your code from FixSizeThreadPool to build a own thread pool executor.

You can not submit some task with timeout. What you can do is when you submit your task your get a Future object. You can keep this reference in some Map and pole and see if the task keeps running past your timeout. If so you can use the method cancel() of class Future.
Alternatively, your own task when it starts running places its own current thread into some Map visible to your main (submitting) thread. Also if you see that your task didn't finish in time (again poling) you can try and interrupt your thread. In either case your submitted task should be able to react to interrupt() method of Thread class. I actually implemented this alternative way. If you go this way, test a LOT... :)

I think Future.get(timeout, unit) method can manage async timeout. Following example can work on my local.

@SpringBootApplication
@EnableScheduling
@EnableAsync
public class AsyncTimeoutExampleAppliation {

    private final  MyService myService;
    public AsyncTimeoutExampleAppliation(MyService myService) {
        this.myService = myService;
    }

    public static void main(String[] args) {
        SpringApplication.run(AsyncTimeoutExampleAppliation.class, args);
    }

    @PostConstruct
    void postConstract(){
        asyncCall();
    }


    public void asyncCall(){
        try {
            String result = myService.doSomething()
                    .get(10, TimeUnit.SECONDS);

            System.out.println(result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
    }


    @Service
    public static class MyService {

        @Async
        public Future<String> doSomething() throws InterruptedException {
            TimeUnit.SECONDS.sleep(60);
            return CompletableFuture.completedFuture("Finished");
        }
    }

}

We can get TimeoutException 10 seconds after application starts.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM