Skip to content
Advertisement

Why can’t I add tasks to the thread pool the second time in Java?

I create a thread pool to deal with the task, after deal with the task,I find I can not add and start the other task? How to fix it? If I change the executor by executor = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new NamedThreadFactory("timeOutThread")); ,It will run OK.But if the task is canceled because of timeout,do this will cause memory leak?

JavaScript

Advertisement

Answer

I can reproduce your error with the following simplified code:

JavaScript

Which gives this output:

JavaScript

The problem is caused by the queue being too small. The LinkedBlockingQueue is created with a capacity of only one, but three tasks are submitted to the pool at once. So, the question becomes, why does it only fail on the second call to invokeAll?

The reason has to do with how ThreadPoolExecutor is implemented. When an instance is first created, none of the core threads are started. They are started lazily as tasks are submitted. When the submission of a task results in a thread being started, that task is immediately given to the thread. The queue is bypassed. So, when invokeAll is called the first time, each of the three core threads is started and none of the tasks go into the queue.

But the second time invokeAll is called, the core threads have already been started. Since submitting the tasks does not result in a thread being created, the tasks are put into the queue. But the queue is too small, resulting in the RejectedExecutionException. If you’re wondering why the core threads are still alive despite the keep-alive time being set to zero, that’s because core threads are not allowed to die due to timeout by default (you have to explicitly configure the pool to allow that).

You can see this lazily-started-core-threads is the cause of the problem by modifying the code slightly. Simply adding:

JavaScript

Just after creating the pool causes the first call to invokeAll to now fail with a RejectedExecutionException.

Also, if you change the queue’s capacity from one to three, then the RejectedExecutionException will no longer occur.


Here’s some relevant documentation:

Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing:

  • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement