Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6452337

ThreadPoolExecutor should prefer reusing idle threads

XMLWordPrintable

    • Icon: Enhancement Enhancement
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 6
    • core-libs

      Currently, ThreadPoolExecutor will aggressively create new threads when
      poolSize < corePoolSize, even when an idle thread is available.
      We should consider a way to enable the opposite behavior, even at the risk
      of occasionally leaving a task temporarily unserviced in the queue.
      This option would be used with allowCorePoolThreadTimeOut.

      Excerpts from email follow:

      I appreciate all your replies, and I definitely realize the design
      tradeoffs that need to be made for the sake of efficiency. I believe I
      tried to hack in the below "poll() before idling thread" + "keep approx
      idle count" approach into the JDK code, though I was not very familiar
      with the code and gave up quickly.

      Briefly, my use-case for having a thread pool behave as requested is
      basically:

      1) multiple thread pools (10 or so)
      2) each with specific priority / thread group / naming / specific Thread
      subclass factories
      3) desire to let each pool grow very large (~1000 threads) due to
      possibility of slow I/O in one or more of the pools & desire for maximum
      concurrency in the tasks
      4) pools hand off work between each other for roughly ~1000 concurrent
      tasks

      By not having #4 from previous message, the number of threads in the
      system can peak up to 10K threads, even though there are only 1000
      concurrent tasks. The conflicting needs are: 1) to have a large CORE
      size for the pools to allow many concurrent threads when the I/O is
      slow, but 2) to keep the average pool size low when the I/O is fast
      (normal case), all the while 3) keeping the TOTAL thread count (globally
      and per-pool) as low as reasonable.

      I won't say the architecture is ideal, but it is much easier to swap out
      thread-pool implementations than it is to rewrite the app. Hope this
      clarifies things a little.


      Best Regards,

      Patrick


      -----Original Message-----
      >
      >>>>IE I would like a:
      >>>>
      >>>>1) potentially infinite queue of tasks (currently using a
      >>>>LinkedBlockingQueue)
      >>>>
      >>>>2) executed by up to MAX threads concurrently
      >>>>
      >>>>3) which go away after a period of inactivity, allowing the pool to
      >>>>shrink to MIN (or zero) size
      >>>>
      >>>>4) preferring to use already existing threads rather than creating new

      ones

      >>>>
      >
      >>
      >>
      >> All but (4) are straightforward.
      >>
      >> By (4) I assume you mean to use an existing thread only if
      >> it is "idle". This is just about impossible to deterministically
      >> guarantee, at least when using an unbounded queue.


      >> The pool cannot know for sure whether an existing thread is idle.
      >> It only knows whether there are fewer or more than target number
      >> of threads, and whether the queue is full, which it never is for
      >> unbounded ones. Problematic cases include those where an
      >> idle-looking thread is in the process of dequeuing a task and
      >> so will momentarily run it. You might be content with an
      >> implementation that ignores such cases and approximates (4)
      >> by for example, counting threads blocked on workQueue.take.
      >> But it would probably be a bad idea for us to add such capabilities --
      >> they invite a stream of bug reports when "approximately" doesn't
      >> cover particular use cases well. (For example, here, it is hard
      >> to avoid the pathology of a large number of threads trying
      >> to submit tasks at the same time, and all of them thinking that
      >> they don't need to create a new thread because there is an idle one.)


      I'm sympathetic to Patrick's suggestion; users generally
      assume that ThreadPoolExecutor prefers reusing idle
      threads. I think we can avoid the above pathology by
      simply checking queue size after enqueuing,
      and changing our minds and adding a
      new thread if the queue size is ever greater than 1.
      Then at worst there will be one excess task waiting while all
      workers are busy.

      If we maintained good idleTaskCount statistics, we
      could be more aggressive and only add a new thread if
      idleTaskCount() < workQueue.size().

      Maintaining an idle task count seems relatively expensive,
      especially if the pool size is large.

      Perhaps we could optimize it by
      Try queue.poll(); if a task is available immediately, run it.
      else increment idleCount while using timed poll() or take().

      There are lots of design choices here.

      Martin


      >> ThreadPoolExecutor tries to cover as wide a set of use cases as
      >> can all be handled by the same overall design. It does cover most
      >> common uses. But even at that, we have had to revamp parts of the
      >> implementation now and then to get rid of unwanted unforeseen
      >> interactions among various parameters and methods.
      >>
      >> So, if you really need this behavior, it looks like you will
      >> need a custom implementation. Or you might decide that plain
      >> newFixedThreadPools are OK for your application after all?
      >>
      >> -Doug

            martin Martin Buchholz
            martin Martin Buchholz
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: