1

I have a spring boot application where a users hits an endpoint and I have to acknowledge that I got their request immediately. I need to do some computation on different thread and send them response on a different endpoint after my computation is over. For executing the task on different thread my thread pool configuration looks something like this:

@Configuration
@EnableAsync
public class SpringAsyncMatchingConfig {

    @Bean(name = "threadTaskExecutor") 
    public TaskExecutor getMatchingTaskExecutor() {  
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();  
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setQueueCapacity(0);
        threadPoolTaskExecutor.setMaxPoolSize(15); 
        return threadPoolTaskExecutor;  
    }  
}

While I do the computation I need to hit one of the endpoint which returns me a token id and does some computation. It usually takes 3 to 4 minutes to do the computation. So what I have done is I have mentioned Thread.sleep(30000) and after 30 seconds is completed I again hit the same api with the token id it provided expecting it to give me a result.

while(result == false) {
    Thread.sleep(30000)
    result = callendpoint(tokenid)
}

Suppose my thread pool is exhausted, it reached its maximum size of 15 threads, and some more tasks are provided to the pool, some of my threads will be in 30 seconds sleep state, will those threads be terminated and assigned a new task because I am in sleep (idle) state? Should I add the set keep alive to prevent this from happening?

threadPoolTaskExecutor.setKeepAliveSeconds(120); 

Is this the right thing to do?

Holger
  • 285,553
  • 42
  • 434
  • 765
Saugat Dahal
  • 39
  • 1
  • 10
  • 1
    Speaking from a REST standpoint, whatever you are trying to do isn't for REST Services to do. In retrospect, my advice is for you to accept the request & pass it down to an asyncrhonous task with a call back function which you can do from the other end point. – papaya Oct 30 '20 at 05:24
  • Thank you so much for suggestion @papaya. It is a legacy code and I don't have much time to rewrite the code. I will keep what you said in my mind when I code in future. Thank you so much once again. – Saugat Dahal Oct 30 '20 at 13:42
  • 2
    An idle thread is a threat *not* performing any task. A task is anything you submitted, whether it bears a `sleep` call or not. A thread pool implementation will not dare to interrupt your task, just because it contains a `sleep` call. Therefore, the keepalive time has no relevance here. – Holger Oct 30 '20 at 16:20
  • 1
    I'm confused by your question. "While I do the computation..." you do something that does some computation? What thread is doing the 30000ms sleep? The short answer to your question is that the threads are never magically killed by the thread pool just because they happen to be asleep. – Gray Nov 01 '20 at 16:49
  • https://stackoverflow.com/a/69259906/5324721 – Kumar Ashutosh Oct 11 '21 at 19:29

1 Answers1

1

Suppose my thread is exhausted it reached maximum size of 15 and some more tasks are provided to the thread, some of my thread will be 30 seconds sleep state

This can happen. Such queue is implemented using LinkedBlockingQueue and the behavior is as follows (source Spring Framework 5.3.X reference documentation: 7.4.2. The executor Element):

  1. If the queueCapacity is reached then the executor creates a new thread beyond the corePoolSize up to maxPoolSize.
  2. If the queueCapacity is also reached with maxPoolSize number of threads, the task is rejected.

The default capacity is unlimited (in fact Integer.MAX_VALUE) which means only corePoolSize number threads will be allocated. If you want to gap the queue capacity, remember to set maxPoolSize a bit higher than corePoolSize. To ensure no task will be rejected, you have to find a suitable balance of the number of core and max threads. Of course all these numbers heavily depend on the expected throughput. With an "unlimited" queue capacity, you don't need to worry about that, however, the performance-tuning is a bit limited as well.


threadPoolTaskExecutor.setKeepAliveSeconds(120);

Is this the right thing to do?

I don't think so. Speaking to the snippet above this one, note that Thread.sleep(30000) with such long time doesn't help you with effective result polling which can be handled thorugh the CompletableFuture instead. Use CompletableFuture::get(long timeout, TimeUnit unit) to stop the thread when a result is not available after 5 minutes. See below what you can do:

@Component
public class AsyncClient {

    @Async
    public CompletableFuture<Result> fetchResult(String tokenId) {
        Result result = callEndpoint(tokenId);                      // a long call
        return CompletableFuture.completedFuture(results);
    }
}
@Service
public class TokenService {

    private final AsyncClient asyncClient;

    public Result callEndpoint(String tokenId) 
        throws InterruptedException, ExecutionException, TimeoutException 
    {
        CompletableFuture<Result> completableFuture = asyncClient.fetchResult(tokenId);
        return completableFuture.get(5, TimeUnit.SECONDS);
    }
}

Finally, let me also remind you three basic rules of using @Async:

  • it must be applied to public methods only
  • it cannot be called from the same class as defined
  • the return type must be either void (more suitable for jobs) or Future
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • 1
    Thank you so much for your response. I really appreciate your help. – Saugat Dahal Oct 30 '20 at 16:28
  • I had one more question if you don't mind so in one of my program I have used completeable future to call the end points. ``` Completeable future = CompleteableFuture.supplyaysnc(() -> function(id)); ``` The function is in a different class and does some computation and returns me a values. Later I do the future.get() to block the thread and recieve the value. In this case the no. of thread on logs keeps on increasing. Since I am returning the value from the function should not that thread be closed by garbage collector? ``` public int function(id) { dosome; return 1; } ``` – Saugat Dahal Oct 30 '20 at 16:33
  • If it's not related to this question, don't hesitate to post a new question and I or other contributors here would be more than happy to help you :) It would be also better as long as the question doesn't seem clear to me from the comment. – Nikolas Charalambidis Oct 30 '20 at 16:40
  • 1
    No worries. I will post another question. Thank you so much for your time. – Saugat Dahal Oct 30 '20 at 16:42