1

This is the cached thread pool:

new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());

and this is the fixed ThreadPoolExecutor:

new ThreadPoolExecutor( 0, 20, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

The first one can create INTEGER.MAX_VALUE number of threads, which is undesired in my case.

The second one, is incorrect. You cannot use a mininum of zero threads and a maximum of 20 threads with a LinkedBlockingQueue.

From the documentation:

http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html

Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.)

The use of a SynchronousQueue in the first case, CachedThreadPool really serves no purpose as a queue. It will only create threads as needed and need a high upper bound.

A good default choice for a work queue is a SynchronousQueue that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed.

Now, what I am after is this:

What I want is for an executor that you can submit work to, that uses a queue if maxThreads are all busy, but that also allows the threads to go idle, and not take up any resources when there are no work.

The use of:

ThreadPoolExecutor ex = new ThreadPoolExecutor(0, threads, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
ex.setKeepAliveTime(idletime, TimeUnit.MILLISECONDS);

I am not sure what implication it has. The documentation seems to only explain the use of an unbounded LinkedBlockingQueue, which I am not really sure what that means, since the constructor creates one with a max capacity of Integer.MAX_VALUE.

The documentation also states:

(And the value of the maximumPoolSize therefore doesn't have any effect.)

What I want is a minimum thread pool size and a maximum thread pool size that queues up work and lets threads go idle when there is no work.

EDIT

Reading this question and the last part made me consider if one should create a

new ThreadPoolExecutor(20, ??irrelevant??, 60L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());

Which would create 20 threads that goes idle if

ex.setKeepAliveTime(60L, TimeUnit.MILLISECONDS);

is set.

Is this an correct assumption?

mjs
  • 21,431
  • 31
  • 118
  • 200

2 Answers2

2

maybe this helps: https://stackoverflow.com/a/19538899/999043 (How to get the ThreadPoolExecutor to increase threads to max before queueing?)

Community
  • 1
  • 1
Ralf H
  • 1,392
  • 1
  • 9
  • 17
1

What I want is a minimum thread pool size and a maximum thread pool size that queues up work and lets threads go idle when there is no work.

This is precicely what a fixed-thread pool with unbounded queue gives you. In short defining a thread-pool like

new ThreadPoolExecutor( 0, 20, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());

Is all you need. What you get is a thread-pool which has at most 20 threads, and a work queue which can accept an 'infinite' number of tasks.

So what if the queue is empty? The ThreadPool will have the 20 threads still alive and waiting on the queue. When they wait on the queue the threads suspend and do no work.

The one update I would make is to change 0L, TimeUnit.MILLISECONDS to something a bit higher like 1L, TimeUnit.SECONDS. This is the thread-idle period, you should let the thread stay alive for a little longer before you shut it down.

In short, a thread-pool does what I believe you want. There may be something I am missing if so let me know in comments.

John Vint
  • 39,695
  • 7
  • 78
  • 108
  • the documentation for fixedThreadPool() says: The threads in the pool will exist until it is explicitly ExecutorService.shutdown(). Does that mean that they are taking up resources, listening for work? See my edit as well I made on my question. – mjs Jan 16 '15 at 16:02
  • `Does that mean that they are taking up resources, listening for work`? No they are not, a waiting thread (which happens when the queue is empty) is suspended - not using resources - until another thread puts a message on the queue to wake the waiting thread up. – John Vint Jan 16 '15 at 16:03
  • To answer your edit, it can make sense to have a Keep Alive Time > 0. I would probably set it. – John Vint Jan 16 '15 at 16:04
  • The edit also sets the corePoolSize to higher, and ignores the maxPoolSize since keepAlive does not use it. – mjs Jan 16 '15 at 16:06
  • Oh I see. So if `corePoolSize == maximumPoolSize`, then the keep alive time isn't necessary as there is no concept of idling. You need at least the corePoolSize to equals maximumPoolSize or will get an `IllegalArgumentException`. – John Vint Jan 16 '15 at 16:09
  • Hmm, there still remains the ambiguity in the documentation: "Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.)" - I am guessing this only applies to new LinkedBlockingQueue(0) ? – mjs Jan 16 '15 at 16:10
  • If corePoolSize equals maximumPoolSize than the maximumPoolSize doesn't have an effect. Current thread-pool size will start at corePoolSize then group to maximumPoolSize on demand (later shrinking back to corePoolSize on idle). So as you can see, if corePoolSize == maxPoolSize then the number of threads will always equal maxPoolSize, hence no effect. – John Vint Jan 16 '15 at 16:14
  • "You need at least the corePoolSize to equals maximumPoolSize or will get an IllegalArgumentException" - Yes, that's true. So one should rely on corePoolSize then? At least when using setKeepAliveTime. Question now is wether to use the arguments or setKeepAliveTime – mjs Jan 16 '15 at 16:32
  • `Question now is wether to use the arguments or setKeepAliveTime` That question can be answered based on the difference between maximumPoolSize and corePoolSize. If it's greater than 0 then set the `setKeepAliveTime` to something greater than zero. Else set it to zero. – John Vint Jan 16 '15 at 16:34
  • 3
    This is wrong answer. Default implementation of ThreadPoolExecutor in case unbound queue doesn't create more than corePoolSize.(or 1 in case of 0) – wist543 Oct 24 '19 at 14:28
  • This is indeed a wrong answer, as @wist543 has said. The default executor will only create more than the core pool of threads when the queue is full. Since the queue is unbounded, it will never be full and only the core pool of threads will ever be created. – justis Jun 05 '22 at 13:53