Threads, waiting for last one to finish

R

Roedy Green

I have difficulty seeing how you wouldn't be able
to figure out how many threads you propose to start.

I easily do it, it is just that in my case it takes an extra pass.
 
A

Arved Sandstrom

Yes, no, it depends.

It is implementation dependent so it can and do differ between
different vendors and platforms.

Some may provide an option for changing the default.

I have once read that SUN/Oracle Java on Windows and Linux uses
320 KB and 1024KB as default on 32 and 64 bit respectively.

SUN/Oracle Java do provide the -Xss option to change the
default.

Arne
I use the -Xss on occasion for some apps, sometimes it's part of their
tuning docs. Usually I don't worry about thread stack size, not if I've
got a relatively small number (25 certainly being small) of threads.

Not when I need GB of memory for heap, and hundreds of megs for perm gen.

My approach here is my usual approach - go for a simple design, and if a
quick gut-check of performance implications raises no red flags then go
with that for implementation.

AHS
 
D

Daniel Pitts

Can you be more specific about when shutdown and await does
not work?

Arne
I am also interested. However, I will say that I would have used the
ExecutorService and simply used the Future objects to wait for
completion of all tasks. This has the added benefit that you can
actually process the results of the Callables in a single thread, rather
than have them try to update some shared data structure. Using the
Future result is inherently easier to get correct.
 
A

Arne Vajhøj

I use the -Xss on occasion for some apps, sometimes it's part of their
tuning docs. Usually I don't worry about thread stack size, not if I've
got a relatively small number (25 certainly being small) of threads.

Not when I need GB of memory for heap, and hundreds of megs for perm gen.

My approach here is my usual approach - go for a simple design, and if a
quick gut-check of performance implications raises no red flags then go
with that for implementation.

I think I have only used it once and that was a demo to prove that
Tomcat could handle 1000 executing but waiting requests in parallel.

But Roedy seemed concerned and the option is there.

Arne
 
K

Kevin McMurtrie

Arne Vajhøj said:
Can you be more specific about when shutdown and await does
not work?

Arne

There are race conditions in determining the predicted thread states.
There's simply no way to check the work queue plus all of the states of
all of the threads in an atomic manner without locking or using a master
thread. Sun's implementation has almost no locking and no master thread
so it makes mistakes while threads are transitioning states. The
variable sized thread pool is the worst (glitches maybe 10% of the
time!) but the others have rare glitches too. These glitches are fixed
when a new task is queued so a steady stream of tasks appears to work
correctly. What happened with the very last task is a gamble. That's
why you shouldn't use shutdown() to wait for completion.

I'm not going to walk through the code here. It's awful stuff that's
difficult to follow and it changes a little with each JVM release. You
can step through it in a debugger and see that there's some wishful
thinking in there. Careful placement of breakpoints can produce a fault
that doesn't recover when all breakpoints are removed.
 
A

Arved Sandstrom

You can always join() them.

I am surprised that this got just one mention so far. In absence of
Executor (which has some issues, as has been mentioned) this is the most
straightforward way to do it.

// untested and from memory
final Thread[] threads = new Thread[15];

for ( int i = 0; i < threads.length; ++i ) {
final Thread th = new Thread(...);
th.start();
threads = th;
}

for ( final Thread th : threads ) {
th.join();
}

Kind regards

robert

You're both right, of course. One of the hazards - speaking for myself -
of seizing on java.util.concurrent and reading JCIP through several
times is that one may tend to forget the original Java concurrency
constructs.

I think join() satisfies Roedy's problem statement the best; in any case
the latches and barriers have different endgame behaviour.

AHS
 
M

markspace

variable sized thread pool is the worst (glitches maybe 10% of the
time!) but the others have rare glitches too. These glitches are fixed
when a new task is queued so a steady stream of tasks appears to work
correctly. What happened with the very last task is a gamble. That's
why you shouldn't use shutdown() to wait for completion.

I'm not going to walk through the code here. It's awful stuff that's


Hmm, could you produce a code example that glitches 10% of the time?
Otherwise I'm afraid that I'll have to file your claims under "internet
b.s." There's no way I'm going to believe that Oracle's code is that
buggy just on hearsay.
 
K

Kevin McMurtrie

markspace said:
Hmm, could you produce a code example that glitches 10% of the time?
Otherwise I'm afraid that I'll have to file your claims under "internet
b.s." There's no way I'm going to believe that Oracle's code is that
buggy just on hearsay.

I can prove it to you when you're paying me for it. Here are some bugs
with the variable sized thread pool:

With short tasks on a system having many CPU cores, the time it takes a
thread to be seen as idle becomes large compared to the task execution
time. The result is the needless creation of more threads The surge of
extra threads steals CPU time from existing threads, making the problem
worse. You can demonstrate this by feeding a fixed number of rapidly
completing tasks into a thread pool and then checking the pool size. A
common symptom can be seen in Tomcat on very large servers, where the
connection handler pool suddenly jumps from ~100 to thousands of threads.

The idle timeout doesn't work. Threads are used round-robin so they're
never idle as long as tasks keep coming at a rate of timeout/threads.
For a timeout of 60 seconds, 100 tasks per second will keep a pool of at
least 6000 threads alive. Combine this with the previous bug and you
can see that the variable sized pool is very broken.

Scheduled tasks do not expand the pool size so they starve a thread pool
of workers. Read the code for ScheduledThreadPoolExecutor if you'd like.

java.util.concurrent.Delayed is defined in relative time and must
implement Comparable<Delayed>. It's impossible to sort tasks using
relative time so Sun hacked it. Look at
java.util.concurrent.ScheduledThreadPoolExecutor.ScheduledFutureTask.comp
areTo(Delayed). Any implementation other than
ScheduledFutureCallableLink does not work reliably.

Fixed size pools have some problems too but they're less frequent and
unobtrusive. You'd probably never hit them unless you're using
shutdown() rather than Future.get() to wait for task completion.
 
K

Kevin McMurtrie

markspace said:
Hmm, could you produce a code example that glitches 10% of the time?
Otherwise I'm afraid that I'll have to file your claims under "internet
b.s." There's no way I'm going to believe that Oracle's code is that
buggy just on hearsay.

I hit another one today and it's directly related to the original post.
The shutdown() method causes a spin loop bug in
ThreadPoolExecutor.getTask().

This is wrong:
if (state == SHUTDOWN) // Help drain queue
r = workQueue.poll();

It should be:
if (state == SHUTDOWN) // Help drain queue
return workQueue.poll();


This was very frustrating because CPU was at 400% but it wouldn't show
up in any Java profiler. I had to pause all threads and start them up
one at a time.

shutdownNow() doesn't cause the bug.
 
R

Robert Klemme

Fixed size pools have some problems too but they're less frequent and
unobtrusive. You'd probably never hit them unless you're using
shutdown() rather than Future.get() to wait for task completion.

I was going to add that most of the issues of the implementation we are
talking about stem from the fact that the number of active threads is
adjusted all the time, i.e. threads are created and die (or at least
that was the intention) all the time.

For most real world problems I am aware of it is sufficient to create
all threads upfront and let them block waiting for work. You are
expecting to use them anyway at some point in time (that's why you set a
specific max) so at least when load is highest the biggest number of
threads is active anyway. Other resources (memory) have to be adjusted
to that fact anyway and if the pool is idle there will be enough memory
to keep those blocking threads around.

The only other scheme that seems to be reasonable to me is to create
threads on demand and never let them die before pool shutdown. That
would at least ensure low overhead for low load situations. But even
this scheme has some overhead in terms of locking already.

My preferred way for shutdown of thread pools is a stop element of which
as many instances as threads around are inserted into the queue. Every
worker threads which reads it terminates itself. That way you do not
need any additional locking and just join all active threads before you
let the shutdown method return. The details of proper shutdown are
still tricky because one can introduce a number of nasty race conditions
which will leave unprocessed items in the queue.

There are many applications though where production of work for the pool
is stopped before the pool is shut down so shutdown race conditions are
not an issues for those types of applications.

Kind regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top