Interrupted IO and AWT

Jeff Sturm jsturm@sigma6.com
Tue Mar 21 12:17:00 GMT 2000


"Boehm, Hans" wrote:
> My general impression is that if you don't care much about fairness (and I
> suspect we usually don't), spin locks with exponential back-off work quite
> well, in that they tend to automatically reduce contention to a reasonable
> level.  (It does seem to have the disadvantage that with lots of contention,
> you sometimes see individual threads going to very long backoffs.  The net
> result is that one or two threads keep the lock for long periods, thus
> reducing the need to move the cache line back and forth.  This is probably
> the right thing for throughput, but not fairness.)  I tend to implement
> short backoff intervals as spins, and longer ones using some sort of sleep
> primitive.

A very large number of locks are extremely short duration, in my
experience.  It makes sense to just spin on them... since the
probability of contention approaches zero as the duration shrinks,
fairness may be of negligible concern for locks that cover just a few
instructions.  This is especially true of a uniprocessor, in which
contention only takes place if a thread is preempted within a critical
section.

Even on SMP, there is no value to using suspend/resume on a thread that
will claim the lock just as fast by spinning, if the duration is much
less than a millisecond there's little chance the spinning processor
could have done any useful work anyway, given the overhead of system
calls and context switching.

Unfortunately Java supports just one synchronization primitive which
must behave reasonably in all scenarios, even if (e.g.) a thread blocks
on I/O.  But large portions of libgcj are C++, in which case we have
more options.  All critical sections in libgcj are currently handled
with _Jv_MonitorEnter/_Jv_MonitorExit, (outside of boehm-gc, which
relies on a local spinlock implementation).  There may be room for
improvement there.

> For some strange reason, that's a scheme that's rarely discussed in the
> literature.  Does anyone have some real measurements that include something
> like this scheme?

No.  What would you suggest measuring?  I'm interested in it but
somewhat lacking in experience.

> I'm still looking at a scheme that maintains a global, non-resizable hash
> table of locks, with no per-object space for locks.  (Collisions do force
> memory allocation.)  My impression is that shrinking objects is very
> worthwhile, since it increases the effectiveness of the cache, and decreases
> memory bandwidth requirements.  Also I suspect that many Java locks are very
> short-lived.  Thus it's important to avoid memory allocation when an object
> is first locked (statistics anyone?).  Again this probably favors throughput
> over predictability, fairness, etc.  But fairness doesn't help much if the
> throughput goes to zero.

Sounds interesting.

In the Java code we develop, I consider long-term synchronization blocks
a bug, and favor rewriting them using a Mutex implementation we have
based on Doug Lea's concurrent package.  By keeping synchronized blocks
short we can tune the algorithm (whenever possible) and avoid deadlock
conditions.  Our Mutex class has some deadlock detection built in. 
OTOH, deadlocks around Java monitors are easy to produce and extremely
difficult to debug.

That's not to say our code is typical... I fear that long synchronized
blocks are common in Java code because that is the path of least
resistance.  Coarse-grained locking simply takes more work.

-- 
Jeff Sturm
jsturm@sigma6.com


More information about the Java mailing list