Say I have some code like so:
private static Thing t = null;
private static final Object lock = new Object();
public static void foo() {
if(t == null) { //Netbeans warning on this line.
synchronized(lock) {
if(t == null) {
t = new Thing();
}
}
}
...Do stuff with t...
}
Now, foo is hosted on a server, and could possibly be called by many users at the same time. The purpose of the function foo is to initialize t if it hasn’t been already, otherwise, use the initialized instance to do whatever stuff needs to be done.
However, Netbeans gives me a warning on code like this:
Remove the outer conditional statement
With a tooltip:
Double-checked locking
The point of the double checked locking is to avoid a bottleneck waiting for people to release the lock. If the only check to see if t was null was inside of the synchronized block, everybody would have to wait for their chance to see if t was null, and everybody-1 would effectively wait for nothing (because t wouldn’t be null). Hence the outer conditional.
With it, say, by some miracle, 5 people resolve t == null to true at the same time. One user will get the synchronized lock, see that t == null is still true, and initialize it. Then, that user will release the lock, and the next user will lock on it, and almost immediately release it (as t != null anymore), this will continue until the 5 users have passed through it. All the while, other users connecting jump over the synchronized lock, as the first user in made t != null.
So, why is Netbeans complaining? This seems like the best way to do it AFAIK.
Thoughts?
It’s complaining because as written, it’s unsafe – or at least may be.
Because
tisn’tvolatile, it may be possible for a reader thread to read the new value oftwhileThingis still being constructed. That would be a bad move – the use of partially-constructed objects is hideous.Before Java 5’s new memory model, it wouldn’t even have been thread-safe with the field marked as
volatile. With the new memory model that’s safe in certain situations, potentially even withoutvolatile, but personally I’d avoid it anyway:Usually initializing as part of the static initializer is good enough:
For other cases there are alternative tricks with nested classes which still use the laziness of static initializers, but all this single property to be lazy even when the other members are accessed.
For a lot more information on the old brokenness of DCL, read The “Double-Checked Locking is Broken” Declaration. (See the end section for the current situation as of JDK 5 – but think about whether you really want maintainers of your code to be thinking about all of this.)