2

At the moment I'm creating fixed thread pool using the Executor service like

executor = Executors.newFixedThreadPool(coreAmount);

While this is fine, I was wondering if it's possible to keep the behaviour of creating a number of threads and change the max pool limit so that if all the threads are in use to create a new thread and use it instead of waiting for one of the threads to terminate to start.

So for example if 8 threads are created and are being used, 9th task enters I want it to create a new thread in addition to the 8 currently in use.

It seems newCachedThreadPool() has the behaviour but I also want the ability to create number of threads similar to newFixedThreadPool(int nThreads)

A.A
  • 743
  • 1
  • 8
  • 20
  • 1
    No. Why would you want to have the mixture of two? – Oleg Sklyar Mar 04 '18 at 21:29
  • I know that I will use a minimum amount of threads, however there maybe case where it's more than anticipated – A.A Mar 04 '18 at 21:30
  • you can define a minimum when you create a [`ThreadPoolExecutor`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ThreadPoolExecutor.html#ThreadPoolExecutor-int-int-long-java.util.concurrent.TimeUnit-java.util.concurrent.BlockingQueue-) yourself, `Executors.newXYZ` is just a shortcut for that. `corePoolSize` is the minimum. See also https://stackoverflow.com/a/16162065/995891 – zapl Mar 04 '18 at 21:34
  • I noticed newScheduledThreadPool() has unlimited max size, would it be a good idea to use it, is it usable if you don't want to delay/schedule anything and just use it as normal threads? – A.A Mar 04 '18 at 21:42

3 Answers3

5

Maybe you can use the ThreadPoolExecutor class. It is an ExecutorService and has the concept of core pool count and max pool count. It also has other features that make it more customizable than the Objects returned by Executors.

Below is an example.

    int coreAmount = 8;

    ExecutorService executor;

    //executor = Executors.newFixedThreadPool(coreAmount);

    int overrun = 4;
    int maxWorkCount = 1_000;
    executor = new ThreadPoolExecutor(coreAmount, coreAmount + overrun, 1, TimeUnit.MINUTES, new ArrayBlockingQueue<>(maxWorkCount));

Here is more info about the params passed in the constructor in the example above.

corePoolSize - the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set

maximumPoolSize - the maximum number of threads to allow in the pool

keepAliveTime - when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.

unit - the time unit for the keepAliveTime argument

workQueue - the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.

Jose Martinez
  • 11,452
  • 7
  • 53
  • 68
1

Like you said, a cached thread pool is exactly what you're looking for. From it's documentation,

Creates a thread pool that creates new threads as needed, but will reuse previously constructed threads when they are available. These pools will typically improve the performance of programs that execute many short-lived asynchronous tasks. Calls to execute will reuse previously constructed threads if available. If no existing thread is available, a new thread will be created and added to the pool. Threads that have not been used for sixty seconds are terminated and removed from the cache. Thus, a pool that remains idle for long enough will not consume any resources.

(Emphasis: mine)

So for example if 8 threads are created and are being used, 9th task enters I want it to create a new thread in addition to the 8 currently in use.

This is exactly the case with Executors#newCachedThreadPool, as seen from its documentation above.


Here is what you can use if you want to emulate a cached thread pool, but with a minimum amount of 8 threads:

ExecutorService service = new ThreadPoolExecutor(8, Integer.MAX_VALUE,
                                                 60L, TimeUnit.SECONDS,
                                                 new SynchronousQueue<Runnable>());
Jacob G.
  • 28,856
  • 5
  • 62
  • 116
  • Yeah I understand this, however I specified in the question I also want the ability to preset a min number of threads before hand. Rather than creating 8 new threads one at a time (which I assume cached does), I want to create 8 threads before any tasks is executed and once that limit has hit begin working like cached – A.A Mar 04 '18 at 22:11
  • Why would you want 8 idle threads to exist if they're not executing anything? – Jacob G. Mar 04 '18 at 22:13
  • Because I want a threadpool of 8 threads for when I expect 8+ tasks to come in? Isn't the whole point of threadpools is to remove the overhead of creating threads constantly? – A.A Mar 04 '18 at 22:15
  • That's exactly what `Executors#newCachedThreadPool` will do once those 8 tasks arrive. I feel like you think that creating the threads beforehand will somehow speed up your program, which is not true. Any change will be negligible, especially for 8 threads. It'll be even more negligible once Java 10 arrives in 2 weeks: http://openjdk.java.net/jeps/312. If you've bench-marked this properly and have determined that creating threads (that are cached!) is slowing your program down, I'll provide you with an optimal method, but this would more than suffice for now. – Jacob G. Mar 04 '18 at 22:16
  • Forgive me if I'm wrong as I'm still pretty new to threadpools. From my understanding creating threads have an overhead which can be minimized from using threadpools, if I expect a min of 8 tasks I can pre-create these 8 threads in threadpool to minimize thread creation over-head? cachedpool doesn't know how many min tasks to expect so I'm expecting a greater overhead vs fixed? – A.A Mar 04 '18 at 22:19
  • It's okay. Creating threads does add overhead, but for only 8, it's negligible. According the documentation above, the threads will be cached for 60 seconds after completion so that they can be reused instead of recreated. If you always have a minimum of 8 tasks executing concurrently, then this won't be an issue, as you'll have to create 8 threads either way. – Jacob G. Mar 04 '18 at 22:22
  • I specified 8 for the sake of the question, it could be any number in my case. – A.A Mar 04 '18 at 22:23
  • Like I said, if you always have a minimum amount of tasks executing concurrently, then that amount of threads will exist at any time. If there is a span of more than 60 seconds where no tasks are submitted, then go with a `ThreadPoolExecutor`, as discussed in [Jose's answer](https://stackoverflow.com/a/49100881/7294647). – Jacob G. Mar 04 '18 at 22:25
  • @A.A Since you seem to really want to create these threads before tasks arrive, I've edited my answer. – Jacob G. Mar 04 '18 at 22:32
  • @JacobG....to have the threads created before tasks arrive...you would also have to call the "prestartAllCoreThreads()" methods. – anish Mar 05 '18 at 08:23
1

This is available in the ThreadPoolExecutor using Core Pool & Maximum Pool size values: From the javadoc, you can see that:

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.

By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks.

Most typically, core and maximum pool sizes are set only upon construction, but they may also be changed dynamically using setCorePoolSize(int) and setMaximumPoolSize(int).

So, in your example, you would need to set the 'corePoolSize' to the value of 8. You would then need to set the 'maximumPoolSize' which would then serve as the upper-bound to the pool. Also, as in the javadoc, these values can be altered dynamically.

Community
  • 1
  • 1
anish
  • 482
  • 2
  • 8