Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ExecutorService that scales threads then queues tasks

Is there an ExecutorService implementation that behaves like a thread pool with the following characteristics?

  1. There are always at least X active threads.
  2. If a task is submitted and all active threads are busy, a new thread is started, up to Y threads.
  3. If a task is submitted and all Y threads are busy, the task is queued.
  4. If no new tasks are submitted, the pool scales back down to X active threads.

Pretty standard thread pooling behavior. You'd think that ThreadPoolExecutor would handle this, but

executorService = new ThreadPoolExecutor(
    2, 10, // min/max threads
    60, TimeUnit.SECONDS, // time of inactivity before scaling back
    new SynchronousQueue<Runnable>()); // task queue

will throw an exception if more than 10 tasks are submitted. Switching to a LinkedBlockingQueue will never scale up past the two minimum threads, unless you limit the size like new LinkedBlockingQueue<Runnable>(20), in which case there will be two threads handling 1-20 tasks, 2-10 threads handling 21-30 tasks, and an exception for more than 30 tasks. Not pretty. A fixed thread pool, meanwhile, will never scale down inactive threads.

So, to get what I'm after, can I use a different kind of BlockingQueue or fiddle with some other setting I've missed? Is there another ExceutorService implementation that is better suited (which?), or am I better off rolling my own by overriding the execute() method of ThreadPoolExecutor?

like image 581
aznan Avatar asked Apr 29 '15 12:04

aznan


1 Answers

Unfortunately, the answer is no. About the best you can do with what is in the jre is to effectively not have a thread floor, only a ceiling. This can be accomplished by allowing core threads to timeout.

ThreadPoolExecutor tpe = new ThreadPoolExecutor(10, 10, 60, TimeUnit.Seconds, new LinkedBlockingQueue<Runnable>());
tpe.allowCoreThreadTimeOut(true);

Because the core size is 10, a new thread will be started when a task is submitted until 10 threads are active. After that, tasks will queue in the LinkedBlockingQueue. If a thread has been inactive for 60 seconds it will terminate.

The behavior you want is possible by writing a class implementing both BlockingQueue and RejectedExecutionHandler, which checks the ThreadPoolExecutors current state before determining if the task should be added to the queue or rejected.

like image 58
Brett Okken Avatar answered Nov 15 '22 22:11

Brett Okken