Patrick Eger | 5 Oct 21:24 2005

ThreadPoolExecutor

Hi, great work on the util.concurrent package, we have converted from to
Oswego package entirely, however there remains one missing piece that I
could not figure out how to do. Basically, we need a ThreadPoolExecutor
with the following properties:

1) {infinite | bounded} linked list request queue, where the bound is
really high to avoid OOM (100000+)
2) MIN of 0 threads in pool
3) MAX # of threads in pool, configurable on pool create
4) reuse existing threads if available
5) allow pool to shrink to zero threads if there are no outstanding
requests

This was possible in the old Oswego package, but now in the JDK, these
all seem possible except for #4, which conflicts with the documentation
(and class ThreadPoolExecutor) here:

"When a new task is submitted in method execute(java.lang.Runnable)
<http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPool
Executor.html#execute%28java.lang.Runnable%29> , and fewer than
corePoolSize threads are running, a new thread is created to handle the
request, even if other worker threads are idle. If there are more than
corePoolSize but less than maximumPoolSize threads running, a new thread
will be created only if the queue is full"

Because LinkedBlockingQueue is never full (or when it is its already
HUGE), no combination of corePoolSize, maximumPoolSize seems to allow
this.

Basically, the problem is:
(Continue reading)

Joe Bowbeer | 5 Oct 22:38 2005
Picon

Re: ThreadPoolExecutor

I've probably missed something but it occurs to me that you want to be using maxPoolSize instead of corePoolSize.

In other words, have you tried the following?

corePoolSize = 0
maxPoolSize = MAX
keepAliveTime = tuned so that pool shrinks to 0 when executor is idle


On 10/5/05, Patrick Eger <peger <at> automotive.com> wrote:
Hi, great work on the util.concurrent package, we have converted from to
Oswego package entirely, however there remains one missing piece that I
could not figure out how to do. Basically, we need a ThreadPoolExecutor
with the following properties:

1) {infinite | bounded} linked list request queue, where the bound is
really high to avoid OOM (100000+)
2) MIN of 0 threads in pool
3) MAX # of threads in pool, configurable on pool create
4) reuse existing threads if available
5) allow pool to shrink to zero threads if there are no outstanding
requests


This was possible in the old Oswego package, but now in the JDK, these
all seem possible except for #4, which conflicts with the documentation
(and class ThreadPoolExecutor) here:

"When a new task is submitted in method execute(java.lang.Runnable)
<http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPool
Executor.html#execute%28java.lang.Runnable%29> , and fewer than
corePoolSize threads are running, a new thread is created to handle the
request, even if other worker threads are idle. If there are more than
corePoolSize but less than maximumPoolSize threads running, a new thread
will be created only if the queue is full"



Because LinkedBlockingQueue is never full (or when it is its already
HUGE), no combination of corePoolSize, maximumPoolSize seems to allow
this.

Basically, the problem is:

If corePoolSize == MAX, idle threads are ignored, and we will always
climb up to MAX threads very quickly, even though we only wan MAX
threads under heavy load
If corePoolSize < MAX, with an infinite (or effectively infinite)
request queue, we have effectively reduced MAX to corePoolSize.


Currently we have a hacked-up JDK 1.6 ThreadPoolExecutor to give us #4
(with corePoolSize==MAX and allowCoreThreadTimeout==true), IE reuse
existing idle threads before trying to create new ones. I'm not certain
if our implementation is correct, it is most certainly ugly:


Hacked into "public void execute(Runnable command)" right after the "if
(runState != RUNNING)" block:
------------------------------------------------------------------------
------------------
//NOTE: Added this for less thread-crazy behaviour
if (workersBlocked.get() > 0) {
        if(workQueue.offer(command)) {
                //HACK: this should protect against the pool shrinking,
should be very rare...
                if (poolSize == 0)
                        addIfUnderCorePoolSize(new Runnable(){ public
void run() {} });

                return;
        }
}
------------------------------------------------------------------------
------------------



Everything is working correctly as per the docs AFAIK, just seemingly
counterintuitively. It seems quite pointless and lower performant to be
creating new threads while existing ones are idle and available to
process work. Is this just a bad interaction between ThreadPoolExecutor
and LinkedBlockingQueue?  Is there another queue type that will work for
me or thread pool option I am missing?



Thanks in advance and please let me know if I am off base or incorrect
here.


Best regards,

Patrick



_______________________________________________
Concurrency-interest mailing list
Concurrency-interest <at> altair.cs.oswego.edu
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Joe Bowbeer | 5 Oct 23:22 2005
Picon

Re: ThreadPoolExecutor

Patrick Eger <peger <at> automotive.com> wrote:
>
> This code is basically the first thing that is run with the  below settings.

Scratch those settings...

Though I think workQueue.offer is executed when corePoolSize == 0.

  if (poolSize < corePoolSize && addIfUnderCorePoolSize(command))
      return;
  if (workQueue.offer(command))
      return;
  int status = addIfUnderMaximumPoolSize(command);
  if (status > 0)      // created new thread
      return;
  if (status == 0) {   // failed to create thread
      reject(command);
      return;
  }

SO you want a decent core pool size, and maxPoolSize is mostly
irrelevant because the queue is effectively unbounded.

If you want the thread pool to shrink to 0 when idle, then you can use

allowCoreThreadTimeOut(true)

which was added in 1.6.

On 10/5/05, Patrick Eger <peger <at> automotive.com> wrote:
>
> Thank you for the quick response.
> Looking at the code  for ThreadPoolExecutor (from JDK 1.6):
>
> public void execute(Runnable command), line  ~876
> -------------------------------------------------
> Runnable r =  addIfUnderMaximumPoolSize(command);
> -------------------------------------------------
>
> This code is basically the first thing that is run with the below settings.
> So basically it will operate the same way it seems, IE adding a
> new thread unconditionally and not reusing existing idle threads.
> I will  write up a quick test to confirm this behaviour but i'm pretty
> sure we explored  this option as well.
>
> Thanks again for your help.
>
> Best regards,
>
> Patrick
>
>  ________________________________
 From: Joe Bowbeer [mailto:joe.bowbeer <at> gmail.com]
> Sent: Wednesday, October 05, 2005 1:38 PM
> To: Patrick  Eger
> Cc: concurrency-interest <at> altair.cs.oswego.edu
> Subject:  Re: [concurrency-interest] ThreadPoolExecutor
>
> I've probably missed something but it occurs to me that you want to
> be using maxPoolSize instead of corePoolSize.
>
> In other words, have you  tried the following?
>
> corePoolSize = 0
> maxPoolSize =  MAX
> keepAliveTime = tuned so that pool shrinks to 0 when executor is  idle
>
> On 10/5/05, Patrick  Eger <peger <at> automotive.com>  wrote:
> Basically, we need a ThreadPoolExecutor with the following properties:
> >
> > 1) {infinite | bounded} linked list request    queue, where the bound is
> > really high to avoid OOM (100000+)
> > 2) MIN of 0    threads in pool
> > 3) MAX # of threads in pool, configurable on pool create
> > 4) reuse existing threads if available
> > 5) allow pool to shrink to zero    threads if there are no outstanding
> > requests
> >
> >
Dawid Kurzyniec | 6 Oct 00:17 2005

Re: Thread safe or not ?

Hanson Char wrote:

> That's exactly right - very astute observation as usual.  It's an 
> outline of a pattern for initializing, in a thread-safe manner, 
> multiple final static constants via (optionally) some wiring of 
> external configuration.
>
> Clients of the final static constants don't need to worry about the 
> external configuration.  The wiring framework (such as Spring) doesn't 
> need to concern about the external configuration actually ends up in a 
> bunch of static final constants.
>
> In the trivial example I gave there is only one static final constant, 
> K.  However, imagine we have multiple system constants.  A 
> SystemParameterBean instance can be initialized once, and can then be 
> used to initialize all the static final constants in SystemParameter, 
> which are then accessed by clients in a simple way.  Like 
> SystemParameter.K, SystemParameter.J, etc.
>
> In such multiple-constant scenario, we can avoid init().getK() and 
> init().getJ(), etc. but simply bean.getK() and bean.getJ().  The 
> init() method implementation, which may incur additional overheads, 
> should be done only once and not linear to the number of system constants.
>
I apologize for such a delay in responding, but, if you move init() to 
the static initializer, you can make the bean a local variable, 
obviating the need to store it in a static field even though you access 
it more than once:

public class SystemParameter {
    public static final long K;
    public static final long L;
    ...
    static {
        SystemParameterBean bean = init();
        K = bean.getK();
        L = bean.getL();
        ...
    }

Regards,
Dawid
Patrick Eger | 5 Oct 22:51 2005

RE: ThreadPoolExecutor

Thank you for the quick response.  Looking at the code for ThreadPoolExecutor (from JDK 1.6):
 
public void execute(Runnable command), line ~876
-------------------------------------------------
Runnable r = addIfUnderMaximumPoolSize(command);
-------------------------------------------------
 
This code is basically the first thing that is run with the below settings. So basically it will operate the same way it seems, IE adding a new thread unconditionally and not reusing existing idle threads.  I will write up a quick test to confirm this behaviour but i'm pretty sure we explored this option as well.
 
Thanks again for your help.
 
 
 
Best regards,
 
Patrick

From: Joe Bowbeer [mailto:joe.bowbeer <at> gmail.com]
Sent: Wednesday, October 05, 2005 1:38 PM
To: Patrick Eger
Cc: concurrency-interest <at> altair.cs.oswego.edu
Subject: Re: [concurrency-interest] ThreadPoolExecutor

I've probably missed something but it occurs to me that you want to be using maxPoolSize instead of corePoolSize.

In other words, have you tried the following?

corePoolSize = 0
maxPoolSize = MAX
keepAliveTime = tuned so that pool shrinks to 0 when executor is idle


On 10/5/05, Patrick Eger <peger <at> automotive.com> wrote:
Hi, great work on the util.concurrent package, we have converted from to
Oswego package entirely, however there remains one missing piece that I
could not figure out how to do. Basically, we need a ThreadPoolExecutor
with the following properties:

1) {infinite | bounded} linked list request queue, where the bound is
really high to avoid OOM (100000+)
2) MIN of 0 threads in pool
3) MAX # of threads in pool, configurable on pool create
4) reuse existing threads if available
5) allow pool to shrink to zero threads if there are no outstanding
requests


This was possible in the old Oswego package, but now in the JDK, these
all seem possible except for #4, which conflicts with the documentation
(and class ThreadPoolExecutor) here:

"When a new task is submitted in method execute(java.lang.Runnable)
<http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ThreadPool
Executor.html#execute%28java.lang.Runnable%29> , and fewer than
corePoolSize threads are running, a new thread is created to handle the
request, even if other worker threads are idle. If there are more than
corePoolSize but less than maximumPoolSize threads running, a new thread
will be created only if the queue is full"



Because LinkedBlockingQueue is never full (or when it is its already
HUGE), no combination of corePoolSize, maximumPoolSize seems to allow
this.

Basically, the problem is:

If corePoolSize == MAX, idle threads are ignored, and we will always
climb up to MAX threads very quickly, even though we only wan MAX
threads under heavy load
If corePoolSize < MAX, with an infinite (or effectively infinite)
request queue, we have effectively reduced MAX to corePoolSize.


Currently we have a hacked-up JDK 1.6 ThreadPoolExecutor to give us #4
(with corePoolSize==MAX and allowCoreThreadTimeout==true), IE reuse
existing idle threads before trying to create new ones. I'm not certain
if our implementation is correct, it is most certainly ugly:


Hacked into "public void execute(Runnable command)" right after the "if
(runState != RUNNING)" block:
------------------------------------------------------------------------
------------------
//NOTE: Added this for less thread-crazy behaviour
if (workersBlocked.get() > 0) {
        if(workQueue.offer(command)) {
                //HACK: this should protect against the pool shrinking,
should be very rare...
                if (poolSize == 0)
                        addIfUnderCorePoolSize(new Runnable(){ public
void run() {} });

                return;
        }
}
------------------------------------------------------------------------
------------------



Everything is working correctly as per the docs AFAIK, just seemingly
counterintuitively. It seems quite pointless and lower performant to be
creating new threads while existing ones are idle and available to
process work. Is this just a bad interaction between ThreadPoolExecutor
and LinkedBlockingQueue?  Is there another queue type that will work for
me or thread pool option I am missing?



Thanks in advance and please let me know if I am off base or incorrect
here.


Best regards,

Patrick



_______________________________________________
Concurrency-interest mailing list
Concurrency-interest <at> altair.cs.oswego.edu
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
David Holmes | 6 Oct 02:17 2005
Picon

RE: ThreadPoolExecutor

Patrick,

Your requirements can't be met directly by the current implementation. The
current design works, as you know, by having a core-pool that can either be
pre-started or lazily created, and which always stays around (in 1.6 idle
core threads can timeout but that just takes you back to the lazy startup
mode). Once you have your core pool you only create more threads (up to max)
if your queue fills up - if the queue never fills then no more threads will
be created. If you reach the maximum size with a full queue the task is
rejected.

> 1) {infinite | bounded} linked list request queue, where the bound is
> really high to avoid OOM (100000+)
> 2) MIN of 0 threads in pool
> 3) MAX # of threads in pool, configurable on pool create
> 4) reuse existing threads if available
> 5) allow pool to shrink to zero threads if there are no outstanding
> requests

Requirement (4) requires a way to detect if threads are "available". But
what does this mean? A worker thread is either executing a task or blocked
waiting for a task to appear in the BlockingQueue. If it is blocked then it
is "available", but to use it you have to submit your task to the queue. To
know if it is blocked you need to keep track of it, which is presumably what
this code is doing:

> //NOTE: Added this for less thread-crazy behaviour
> if (workersBlocked.get() > 0) {
> 	if(workQueue.offer(command)) {
> 		//HACK: this should protect against the pool shrinking,
> should be very rare...
> 		if (poolSize == 0)
> 			addIfUnderCorePoolSize(new Runnable(){ public
> void run() {} });
>
> 		return;
> 	}
> }

However this sort of check requires atomicity that isn't normally present in
the ThreadPoolExecutor. So to do this right requires additional locking
otherwise two incoming tasks can see one available worker and assume the
worker will run their task, when it fact one task will remain in the queue
and the pool could have queued tasks but less than max (or even core)
threads.

So if you really want this you have to pay a price to get it.

> Everything is working correctly as per the docs AFAIK, just seemingly
> counterintuitively. It seems quite pointless and lower performant to be
> creating new threads while existing ones are idle and available to
> process work.

The assumption is that the core pool will be quite steady so if you don't
create a core thread this time, the expected usage means you are going to
create it very soon anyway. If you pre-start the core then you don't create
new threads until your queue is full.

> Is this just a bad interaction between ThreadPoolExecutor
> and LinkedBlockingQueue?  Is there another queue type that will work for
> me or thread pool option I am missing?

It seems to me - and I could be misunderstanding things - that what you want
is a "dual-mode" queue. Set the core size at zero and what you want is to
submit the task to the queue, if a thread is waiting, else create a thread.
This is where you need a synchronous queue - it has no capacity, so if no
thread is waiting then offer() will fail and from the pool's perspective the
queue is "full" and so a new thread will be created if under max. But when
max is reached you now want a queue that has capacity. You could use the
RejectedExecutionHandler to then make your queue (you'd need to define a
custom queue for this) switch to act as a (finite) linked blocking queue. If
the normal queue detects it is empty then it switches back to synchronous
mode. I *think* that would meet your requirements *but* I don't know if what
I just described can actually be implemented. Interesting to think about it
anyway :-)

Cheers,
David Holmes
David Holmes | 6 Oct 05:44 2005
Picon

RE: ThreadPoolExecutor

Patrick,

> There are definite race issues here as well, but it think it is safe in
> that the only problem would be if the pool shrinks to zero size after we
> offer it to the queue.  "if (poolSize == 0)" clause below protects
> against this case I think...

The approximation involved in using the workersBlocked count would
invalidate one of the basic guarantees regarding the creation of core
threads - ie that one will always be created if needed. Without this, for
example, barrier designs that use a pool wouldn't work because tasks may be
left in the queue with all existing workers blocked on the barrier waiting
for the other tasks to arrive - which they won't because they are in the
queue and less than coreSize threads have been created. You would have to
guarantee atomicity using locks.

People have enough trouble understanding the relationship between coreSize,
queue length and maxSize, as it is! Adding another variable to the mix would
be a recipe for potential disaster. That's not to say that there couldn't be
a different TPE that worked as you would like, but it seems unlikely to be a
j.u.c class. I wish there were a clean way to abstract these policy choices
out so that subclasses could modify them, but I can't see a way of doing
this. So cut-paste-modify seems the simplest solution.

> Your queue approach is also interesting but sound much more complex and
> error prone, plus it would really complexify the (currently simple)
> interface between the thread pool and the queue.

Hmmm. There wouldn't be any change in the interface between them - as far as
I can see. You give the pool your special queue and install the queue's
rejected execution handler, then everything "just works". I'm not sure how
the task that triggers the switch from synchronous to non-synchronous mode
gets resubmitted: maybe a simple MyQueue.this.put(), or if necessary hook
the queue to the TPE and let the handler reinvoke execute. I don't *think*
it is that complicated but I won't know unless I try to implement it.

> P.S. I have confirmed via a small test case that a corePoolSize of zero
> will result in submitted tasks *never* executing.

As you would expect - threads beyond the core size only get created when the
queue is full. If the queue can't fill then no threads ever get created. Do
you think this combination should be detected and prohibited?

> P.P.S. Am I still confused and not knowing what I want?  I assumed this
> behaviour is what most people would want for a dynamic work queue
> (0-1000 threads) with bursty request patterns (0-1 million+ on the queue
> ant any given point), but I cannot find much in the archives...

The current design assumes that the core is set such that the expected load
is accommodated, with room for increasing the worker set under heavy load,
by limiting the buffering done by the queue. Having large numbers of threads
in the pool is generally detrimental to overall performce, but may be
necessary if the tasks are expected to block for reasonable periods.
Otherwise with a small core size you would not generally be concerned about
the startup overhead of these threads - whether done eagerly or on demand.

Do you really expect 1000 active threads? How many CPU's are you running on?
Even on a 384-way Azul box I wouldn't expect to need a pool that large. :)

Cheers,
David Holmes

PS. Doug Lea is obviously incommunicado right now or else I'm certain he
would have chipped in - and I expect he will when he can.
Hanson Char | 6 Oct 06:51 2005
Picon

Re: Thread safe or not ?

Yes, thanks.  Indeed the better solution you point out has also been pointed out by Joe Bowbeer in an earlier response to this thread.

Hanson

On 10/5/05, Dawid Kurzyniec <dawidk <at> mathcs.emory.edu> wrote:
Hanson Char wrote:

> That's exactly right - very astute observation as usual.  It's an
> outline of a pattern for initializing, in a thread-safe manner,
> multiple final static constants via (optionally) some wiring of
> external configuration.
>
> Clients of the final static constants don't need to worry about the
> external configuration.  The wiring framework (such as Spring) doesn't
> need to concern about the external configuration actually ends up in a
> bunch of static final constants.
>
> In the trivial example I gave there is only one static final constant,
> K.  However, imagine we have multiple system constants.  A
> SystemParameterBean instance can be initialized once, and can then be
> used to initialize all the static final constants in SystemParameter,
> which are then accessed by clients in a simple way.  Like
> SystemParameter.K, SystemParameter.J, etc.
>
> In such multiple-constant scenario, we can avoid init().getK() and
> init().getJ(), etc. but simply bean.getK() and bean.getJ().  The
> init() method implementation, which may incur additional overheads,
> should be done only once and not linear to the number of system constants.
>
I apologize for such a delay in responding, but, if you move init() to
the static initializer, you can make the bean a local variable,
obviating the need to store it in a static field even though you access
it more than once:

public class SystemParameter {
    public static final long K;
    public static final long L;
    ...
    static {
        SystemParameterBean bean = init();
        K = bean.getK ();
        L = bean.getL();
        ...
    }


Regards,
Dawid


_______________________________________________
Concurrency-interest mailing list
Concurrency-interest <at> altair.cs.oswego.edu
http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest
Patrick Eger | 6 Oct 05:14 2005

RE: ThreadPoolExecutor

Hello and thank you for your response.  I forgot {to mention} the extra
"workersBlocked" variable I added to keep track of the # of threads
waiting for a task to execute.  This obviously adds the extra expense of
the atomic inc/dec pair for every execute() (told you it was hacky :-).
There are definite race issues here as well, but it think it is safe in
that the only problem would be if the pool shrinks to zero size after we
offer it to the queue.  "if (poolSize == 0)" clause below protects
against this case I think...

....
private final AtomicInteger workersBlocked = new AtomicInteger(0);
....
workersBlocked.incrementAndGet();
try {
	// untimed wait if core and not allowing core timeout
	if (poolSize <= corePoolSize && !allowCoreThreadTimeOut)
      	return workQueue.take();

      long timeout = keepAliveTime;
	if (timeout <= 0) // die immediately for 0 timeout
      	return null;
      Runnable r = workQueue.poll(timeout, TimeUnit.NANOSECONDS);
	if (r != null)
      	return r;
	if (poolSize > corePoolSize || allowCoreThreadTimeOut)
      	return null; // timed out
      // Else, after timeout, the pool shrank. Retry
     	break;	
} finally {
	workersBlocked.decrementAndGet();
}
....

It uses the "workersBlocked" as an estimate of whether or not a thread
is immediately available to execute the Runnable, though this will be
incorrect under heavily concurrent calls to execute().  I would lkove to
find a better way to do this, maybe an atomic decrement semaphore-style
"reservation" would allow correctness in the face of multiple concurrent
executes()? This would still be an additional atomic dec/inc pair (one
in execute(), one in the executor thread when it picks the task up).

Your queue approach is also interesting but sound much more complex and
error prone, plus it would really complexify the (currently simple)
interface between the thread pool and the queue. 

I would gladly pay this inc/dec cost (as a configurable option, off by
default for backwards compatibility sake and so should not effect
existing users) for the better (IMO of course) thread creation
behaviour.

Do you guys see this as generally useful?  Would a patch using the
(configurable, off by default and so would only add a cachable read to
the fastpath) inc/dec reservation-style approach be considered for
review (the above is a bit of a hack and probably has some bad
characteristics under heavy concurrent use as David has pointed out)? Or
should I pursue the queue-based approach as suggested?

Thanks again for everyone's help!

P.S. I have confirmed via a small test case that a corePoolSize of zero
will result in submitted tasks *never* executing.  They are infinitely
offer()ed up to the queue but there are no threads available to process.
In effect the corePoolSize becomes the "max" pool size with an infinite
queue, which does not give me the behaviour I desire.

P.P.S. Am I still confused and not knowing what I want?  I assumed this
behaviour is what most people would want for a dynamic work queue
(0-1000 threads) with bursty request patterns (0-1 million+ on the queue
ant any given point), but I cannot find much in the archives...

Best regards,

Patrick

-----Original Message-----
From: David Holmes [mailto:dholmes <at> dltech.com.au] 
Sent: Wednesday, October 05, 2005 5:18 PM
To: Patrick Eger; concurrency-interest <at> altair.cs.oswego.edu
Subject: RE: [concurrency-interest] ThreadPoolExecutor

Patrick,

Your requirements can't be met directly by the current implementation.
The current design works, as you know, by having a core-pool that can
either be pre-started or lazily created, and which always stays around
(in 1.6 idle core threads can timeout but that just takes you back to
the lazy startup mode). Once you have your core pool you only create
more threads (up to max) if your queue fills up - if the queue never
fills then no more threads will be created. If you reach the maximum
size with a full queue the task is rejected.

> 1) {infinite | bounded} linked list request queue, where the bound is 
> really high to avoid OOM (100000+)
> 2) MIN of 0 threads in pool
> 3) MAX # of threads in pool, configurable on pool create
> 4) reuse existing threads if available
> 5) allow pool to shrink to zero threads if there are no outstanding 
> requests

Requirement (4) requires a way to detect if threads are "available". But
what does this mean? A worker thread is either executing a task or
blocked waiting for a task to appear in the BlockingQueue. If it is
blocked then it is "available", but to use it you have to submit your
task to the queue. To know if it is blocked you need to keep track of
it, which is presumably what this code is doing:

> //NOTE: Added this for less thread-crazy behaviour if 
> (workersBlocked.get() > 0) {
> 	if(workQueue.offer(command)) {
> 		//HACK: this should protect against the pool shrinking,
should be 
> very rare...
> 		if (poolSize == 0)
> 			addIfUnderCorePoolSize(new Runnable(){ public
void run() {} });
>
> 		return;
> 	}
> }

However this sort of check requires atomicity that isn't normally
present in the ThreadPoolExecutor. So to do this right requires
additional locking otherwise two incoming tasks can see one available
worker and assume the worker will run their task, when it fact one task
will remain in the queue and the pool could have queued tasks but less
than max (or even core) threads.

So if you really want this you have to pay a price to get it.

> Everything is working correctly as per the docs AFAIK, just seemingly 
> counterintuitively. It seems quite pointless and lower performant to 
> be creating new threads while existing ones are idle and available to 
> process work.

The assumption is that the core pool will be quite steady so if you
don't create a core thread this time, the expected usage means you are
going to create it very soon anyway. If you pre-start the core then you
don't create new threads until your queue is full.

> Is this just a bad interaction between ThreadPoolExecutor and 
> LinkedBlockingQueue?  Is there another queue type that will work for 
> me or thread pool option I am missing?

It seems to me - and I could be misunderstanding things - that what you
want is a "dual-mode" queue. Set the core size at zero and what you want
is to submit the task to the queue, if a thread is waiting, else create
a thread.
This is where you need a synchronous queue - it has no capacity, so if
no thread is waiting then offer() will fail and from the pool's
perspective the queue is "full" and so a new thread will be created if
under max. But when max is reached you now want a queue that has
capacity. You could use the RejectedExecutionHandler to then make your
queue (you'd need to define a custom queue for this) switch to act as a
(finite) linked blocking queue. If the normal queue detects it is empty
then it switches back to synchronous mode. I *think* that would meet
your requirements *but* I don't know if what I just described can
actually be implemented. Interesting to think about it anyway :-)

Cheers,
David Holmes
David Holmes | 7 Oct 00:57 2005
Picon

RE: ThreadPoolExecutor

> Just a little surprising is all, though I'm not sure how the
> ThreadPoolExecutor could know that a queue is non-synchronous?

A check of BlockingQueue.remainingCapacity would tell whether the queue was
bounded or not - but it would be subjective as to how big a value
"unbounded" really is.

> Actually isn't corePoolSize==0 an invalid usage pattern if the queue has
> *any* capacity at all?

With an infinite queue, or a queue you don't expect to fill then it is a
problem. However, you could use a zero core size and a small queue to
effectively "batch" tasks.

ThreadPoolExecutor is quite flexible but a side effect of that is that some
combinations of options make little or no sense.

> What we have is lots of threads active at a time doing things like
> waiting for a database or network response so our pools need to be
> pretty big to accommodate all these IO blocking calls.  I'd love to move
> to an event driven non-blocking IO model but unfortunately key
> technologies such as JDBC don't provide any such options.

I see. At this stage "fixing" the pool is probably a better option than
redesigning the core application architecture.

> I'll just maintain a modified version for myself (and anyone else if
> interested, just let me know).

I'm intrigued enough to try and implement that dual queue set up. :)

Cheers,
David Holmes

Gmane