You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

PooledExecutorWithDMStats has (in general) 2 queues and a rejected execution handler:

  • ThreadPoolExecutor.workQueue: the input queue for the ThreadPoolExecutor. Always a SynchronousQueue. But why?
  • PooledExecutorWithDMStats.blockingWorkQueue: a queue with storage, used if client supplied one with storage, otherwise null
  • ThreadPoolExecutor.handler (rejected execution handler): a policy object invoked (with the task) when workQueue is full and the thread pool is empty (no more threads available)

A perusal of the constructor indicates that if caller supplies a synchronous queue (a queue with no storage) then:

  • workQueue: the synchronous queue provided by the caller
  • blockingWorkQueue: is null
  • rejected execution handler: policy is to run task immediately, directly in the thread calling ExecutorService.submit()

…otherwise (the caller supplied a non-synchronous queue, i.e. a queue with storage):

  • workQueue: a new SynchronousQueue
  • blockingWorkQueue: non-synchronous queue provided by caller
  • rejected execution handler: the policy (BufferHandler) is to queue the task in the blockingWorkQueue. The thread created in the PooledExecutorWithDMStats constructor then competes with task-submitting (client) threads to submit work from this queue to the ThreadPoolExecutor

In regards to the "competing": tasks submitted by the thread adding work directly to the ThreadPoolExecutor's workQueue are not susceptible to rejection. So that's a good thing. That thread (the one optionally spun up in the PooledExecutorWithDMStats constructor) blocks while adding an entry directly to the workQueue via put(). Only tasks submitted through the ExecutorService.submit() interface can be rejected.

This is not "fair" scheduling. Tasks that have been submit()ed and rejected and put in the blockingWorkQueue are not, in general, all processed ahead of new tasks arriving via other threads calling ExecutorService.submit(). The latter can cut in line in front of the thread servicing the blockingWorkQueue. This is not a bug per se. The framework simply doesn't have any fairness contract for these thread pools.

Outstanding Questions

It's not clear why, even if the user supplies a non-synchronous queue to the constructor, PooledExecutorWithDMStats insists upon fabricating a synchronous queue for use as the ThreadPoolExecutor's workQueue.

Darrel Schneider thinks maybe this code also has something to do with core threads and wanting to get all the threads busy before we start buffering requests up in a queue. I think we saw that if we used a non-sync-queue to feed the executor, then it would end up doing all the work in a single thread. Here is one discussion that touches on this issue: https://stackoverflow.com/questions/47650247/synchronousqueue-in-threadpoolexecutor

But this code in AdoptOpenJDK 8 ThreadPoolExecutor.execute() looks like it would perform just if the workQueue was a non-synchronous queue:



  • No labels