It seems to be impossible to make a cached thread pool with a limit to the number of threads that it can create.
Here is how static Executors.newCachedThreadPool is
Doesn't look as though any of the answers actually answer the question - in fact I can't see a way of doing this - even if you subclass from PooledExecutorService since many of the methods/properties are private e.g. making addIfUnderMaximumPoolSize was protected you could do the following:
class MyThreadPoolService extends ThreadPoolService {
public void execute(Runnable run) {
if (poolSize() == 0) {
if (addIfUnderMaximumPoolSize(run) != null)
return;
}
super.execute(run);
}
}
The closest I got was this - but even that isn't a very good solution
new ThreadPoolExecutor(min, max, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()) {
public void execute(Runnable command) {
if (getPoolSize() == 0 && getActiveCount() < getMaximumPoolSize()) {
super.setCorePoolSize(super.getCorePoolSize() + 1);
}
super.execute(command);
}
protected void afterExecute(Runnable r, Throwable t) {
// nothing in the queue
if (getQueue().isEmpty() && getPoolSize() > min) {
setCorePoolSize(getCorePoolSize() - 1);
}
};
};
p.s. not tested the above
You can use ThreadPoolExecutor
as suggested by @sjlee
You can control the size of the pool dynamically. Have a look at this question for more details :
Dynamic Thread Pool
OR
You can use newWorkStealingPool API, which has been introduced with java 8.
public static ExecutorService newWorkStealingPool()
Creates a work-stealing thread pool using all available processors as its target parallelism level.
By default, the parallelism level is set to number of CPU cores in your server. If you have 4 core CPU server, thread pool size would be 4. This API returns ForkJoinPool
type of ExecutorService
and allow work stealing of idle threads by stealing tasks from busy threads in ForkJoinPool.
Per the Javadoc for ThreadPoolExecutor:
If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full. By setting corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool.
(Emphasis mine.)
jitter's answer is what you want, although mine answers your other question. :)
This is what you want (atleast I guess so). For an explanation check Jonathan Feinberg answer
Executors.newFixedThreadPool(int n)
Creates a thread pool that reuses a fixed number of threads operating off a shared unbounded queue. At any point, at most nThreads threads will be active processing tasks. If additional tasks are submitted when all threads are active, they will wait in the queue until a thread is available. If any thread terminates due to a failure during execution prior to shutdown, a new one will take its place if needed to execute subsequent tasks. The threads in the pool will exist until it is explicitly shutdown.
The problem was summarized as follows:
I want something exactly like the cached thread pool (it creates threads on demand and then kills them after some timeout) but with a limit on the number of threads that it can create and the ability to continue to queue additional tasks once it has hit its thread limit.
Before pointing to the solution I will explain why the following solutions don't work:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronousQueue<>());
This will not queue any tasks when the limit of 3 is reached because SynchronousQueue, by definition, cannot hold any elements.
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
This will not create more than a single thread because ThreadPoolExecutor only creates threads exceeding the corePoolSize if the queue is full. But LinkedBlockingQueue is never full.
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 60, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());
executor.allowCoreThreadTimeOut(true);
This will not reuse threads until the corePoolSize has been reached because ThreadPoolExecutor increases the number of threads until the corePoolSize is reached even if existing threads are idle. If you can live with this disadvantage then this is the easiest solution to the problem. It is also the solution described in "Java Concurrency in Practice" (footnote on p172).
The only complete solution to the described problem seems to be the one involving overriding the queue's offer
method and writing a RejectedExecutionHandler
as explained in the answers to this question: How to get the ThreadPoolExecutor to increase threads to max before queueing?
there is one more option. Instead of using new SynchronousQueue you can use any other queue also, but you have to make sure its size is 1, so that will force executorservice to create new thread.