FixedThreadPool vs CachedThreadPool: the lesser of two evils

柔情痞子 提交于 2019-11-26 23:32:40
Trevor Freeman

A CachedThreadPool is exactly what you should use for your situation as there are no negative consequence to using one for long running threads. The comment in the java doc about CachedThreadPools being suitable for short tasks merely suggest that they are particularly appropriate for such cases, not that they cannot or should not be used for tasks involving long running tasks.

To elaborate further, Executors.newCachedThreadPool and Executors.newFixedThreadPool are both backed by the same thread pool implementation (at least in the open JDK) just with different parameters. The differences just being their thread minimum, maximum, thread kill time, and queue type.

public static ExecutorService newFixedThreadPool(int nThreads) {
     return new ThreadPoolExecutor(nThreads, nThreads,
                                   0L, TimeUnit.MILLISECONDS,
                                   new LinkedBlockingQueue<Runnable>());
 }

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
}

A FixedThreadPool does have its advantages when you do in fact want to work with a fixed number of threads, since then you can submit any number of tasks to the executor service while knowing that the number of threads will be maintained at the level you specified. If you explicitly want to grow the number of threads, then this is not the appropriate choice.

This does however mean that the one issue that you may have with the CachedThreadPool is in regards to limiting the number of threads that are running concurrently. The CachedThreadPool will not limit them for you, so you may need to write your own code to ensure that you do not run too many threads. This really depends on the design of your application and how tasks are submitted to the executor service.

Both FixedThreadPool and CachedThreadPool are evils in highly loaded applications.

CachedThreadPool is more dangerous than FixedThreadPool

If your application is highly loaded & demands low latency, better to get rid of both options due to below drawbacks

  1. Unbounded nature of task queue : It may cause out of memory or high latency
  2. Long running threads will cause CachedThreadPool to go out of control on Thread creation

Since you know that both are evils, lesser evil does n't do good. Prefer ThreadPoolExecutor, which provides granular control on many parameters.

  1. Set the task queue as bounded queue to have better control
  2. Have right RejectionHandler - Your own RejectionHandler or Default handlers provided by JDK
  3. If you have something to do on before/after completion of task, override beforeExecute(Thread, Runnable) and afterExecute(Runnable, Throwable)
  4. Override ThreadFactory if thread customization is required
  5. Control Thread pool size dynamically at run time ( related SE question : Dynamic Thread Pool)

So I have a program that spawns threads (~5-150) which perform a bunch of tasks.

Are you sure you understand how threads are actually processed by your OS and hardware of choice? How Java maps threads to OS threads, how that maps threads to CPU threads etc.? I'm asking because creating 150 threads within in ONE JRE only makes sense if you have massive CPU cores/threads underneath, which most likely is not the case. Depending on the OS and RAM in use, creating more than n threads might even result in your JRE being terminated because of OOM errors. So you should really distinguish between threads and work to do by those threads, how many work you are even able to process etc.

And that's the problem with CachedThreadPool: It doesn't make sense to queue up long running work in threads which actually can't run because you only have 2 CPU cores able to process those threads. If you end up with 150 scheduled threads you might create a lot of unnecessary overhead for the schedulers used within Java and the OS to concurrently process them. This is simply impossible if you only have 2 CPU cores, unless your threads are waiting for I/O or such all the time. But even in that case a lot of threads would create a lot of I/O...

And that problem doesn't occur with FixedThreadPool, created with e.g. 2+n threads, where n is reasonable low of course, because with that hardware and OS resources are used with far less overhead for managing threads which can't run anyway.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!