[concurrency-interest] JSR 166 locks

Doug Lea dl@cs.oswego.edu
Sat, 24 Jan 2004 09:20:27 -0500

> * Could one say that LockSupport#park/unpark are semantically
>   equivalent to Thread.currentThread().wait() / someThread.notify() ?

No, they are pure blocking/unblocking operations, that have no
"locking" semantics. For example they do not release and reacquire
locks as Object.wait does.  You'd almost never use park/unpark alone,
but only as the underlying blocking support for other lock, condition,
monitor, synchronization classes.

As the javadocs say, park/unpark are best thought of as replacements
for the deprecated Thread.suspend/resume that eliminate the race
problem that makes suspend/resume unusable.  (Digressing: we
considered simply replacing the old suspend/resume
specs/implementations in class Thread with these new semantics, but
instead hid them away in LockSupport because the methods are
essentially never useful except within other synchronization classes.
Placing them in Thread where people might find and use them by
accident would be a terrible API design choice.)

> * I've been reading a great deal of papers around the days of
>   EVM/early Hotspot about the most efficient way to implement Java
>   monitors (thin locks, ...). The obvious difference, of course, is
>   that JSR 166 does not allow synchronization on every Java object,
>   i.e. the whole space/object header issues do not arise. Besides
>   that, how do the two implementations compare ? Now that CAS and
>   volatile/mbars are available, would you say that the new
>   implementations are up to par with their "native" equivalents ?

The main design goal for new ReentrantLock and related classes was
scalability and responsiveness under contention, which are better than
what you get with native locking on most JVMs.  Performance in
uncontended cases (i.e., pure overhead) was a secondary concern, but
is still good. (Another secondary goal was tunability; for example
being able to optionally force fairness.)  Measurements I've made show
that overhead on uncontended locks varies a fair amount across
platforms and usages. In some cases on some machines they are a little
faster, in others they are a little slower, but in all cases the
differences are small enough not to be a performance issue either way.
For example, if you replace native locks with ReentrantLock or others
in an application with no lock contention, you are unlikely to see
much of a difference. (Although with hotspot, "-client" mode is more
likely to see a slowdown, which is one of several reasons to prefer
"-server" mode for concurrent programs using JSR166 stuff.)  But any
application that encounters contention is likely to improve.

And if you come up with a better locking algorithm, we give you all
the tools to implement it. As you say, just about the only thing you
cannot do that a JVM can (*) is steal bits in object headers and the
like to represent lock status. But there's no motivation for you to so
this sort of thing anyway. Most of the tech papers on Java locking are
in large part trying to deal with the issue that 99% of Java objects
are never locked, so you need a compact but expandible representation
to reduce space consumption of average objects.  But if you are making
your own classes, you know they will be used for locking, so don't
need to be so obsessed about reducing space overhead when locking is
never used.

(*) For completeness: you also cannot do anything that falls outside
the JSR133 Java Memory Model. For example, you cannot do a write with
non-volatile semantics on a volatile variable. There have been a
couple of locking algorithms that involve such things, but they are at
best non-portable. Similarly, we don't support machine-specific atomic
instructions that can't be emulated across the different platforms
Java must be able to run on.

Disclaimer: I only know about performance etc on hotspot, not other
JVMs.  Hotspot is the reference implementation, and, as far as I know,
currently the only implementation. We have standing offers to other
JVM implementors to help them find the best ways to implement JSR166
native atomic, blocking, and timing support.

> * I've been using the util.concurrent package for a long time now, and
>   there is one big drawback of Java-defined locking primitives: it is
>   impossible to spot locking states in thread dumps. Your application
>   deadlocks and you cannot see why. A parked thread has no real
>   association to a lock object anymore. Do you have any plans on VM
>   support for that ? The only option I see is to iterate over all lock
>   objects in the heap and look at their owner -- and that doesn't give
>   me the locking order. Maybe I'll give it a shot with the new
>   JVMTI. Well, all the nice synchronization constructs should save me
>   from deadlocks in the first place ...

Hotspot 1.5 with JVMTI will be aware of park/unpark.  (It also shows
up in stack traces. Try for example a stack dump (control-\ on unix)
on early-access hotspot + current jsr166.jar to see what JSR166 lock
code is doing.)  There is not yet extensive support, but between JVMTI
and the instrumentation methods (like getQueuedThreads) in
ReentrantLock etc, in principle you can de even better than currently
possible in diagnosing and analyzing problems because the lock
implementations and instrumentation methods are in Java proper. It
will probably take a while before people figure out good ways to
exploit this.