Mutant World

Friday, July 15, 2005

A weird particular in java.util.concurrent

I am using J2SE 5, and so far I am quite happy. I got used to the generics syntax, and in general I can write less code to do the same things.

While browsing java.util.concurrent source code, I found this weird pattern:

public class LinkedBlockingQueue extends ...
{
...
private final ReentrantLock takeLock = new ReentrantLock();
...

private void signalNotEmpty()
{
final ReentrantLock takeLock = this.takeLock;
takeLock.lock();
try
{
notEmpty.signal();
}
finally
{
takeLock.unlock();
}
}
...
}

The more I looked at the code, the more it seemed to me unnecessary to declare that final local variable in the signalNotEmpty() method.
The data member is already final, so it's not a question of visibility in multithread environments.

I decided to investigate more, and went back to Bill Pugh's site, which contains a lot of information about the java memory model and its revision through JSR 133, but I had no luck.

Finally I discovered that JSR 166 implementation is also under public domain, here.

The CVS log that added the weird pattern above is marked in this cryptic comment:

"cache finals across volatiles"

Now, if you have some information about, please drop a comment.

This reminds me what JSR 133 experts were saying about the memory model: that not even the JLS writers understood it.
Now I am certain that few more people understand it (e.g. Doug Lea, Bill Pugh), but - alas - I guess they can be counted with just one hand.

I'll let you informed if I found a solution to this arcane.

UPDATE: Doug Lea had the courtesy to answer to my request for clarification. His answer:

"This was a hopefully temporary performance hack due to a limitation in JVMs that don't realize that final fields are an exception to the rule that you must re-read fields after reading a volatile.
These JVMs, including 1.5.0 hotspot are correct wrt the memory model but inefficient unless helped out in this ugly/weird way. Someday these can disappear. It's
only worth bothering (and even then only sometimes, and even then only marginally so)
for performance-critical code like that inside j.u.c.
It's not a recommended practice."

It happens that notEmpty.signal() reads volatile fields and the JVM implementation inefficiently re-reads takeLock to execute takeLock.unlock() in the finally block.

2 Comments:

  • I believe it's a small performance optimisation which goes something like this:

    In 5.0 volatiles have a happens-before edge between reading and writing. Effectively this means that like using synchronized, everything has to be flushed out of registers into main memeory and movement of code is restricted. volatiles are more expensive than they were (but they are useful now).

    Having a variable on the stack means that it is thread-local and therefore does not have to be re-read and can be assumed to reference the same object (so same type and no NPE for instance).

    It is possible to work out that due to the final field semantics that the field does not need to be re-read, but that is asking a bit much of the old compilers.

    By Anonymous Tom Hawtin, at 16 July, 2005 13:37  

  • Besides the hack there is another minor benefit to redeclaring the field that has nothing to do w/ final. By copying the reference locally (Thread stack), subsequent access avoid the getfield opcode that the compiler would otherwise generate.

    By Anonymous studdugie, at 23 July, 2005 23:56  

Post a Comment

<< Home