How does Java Synchronized compare locked objects?
Does synchronized(obj) lock based on the object's memory location or its toHash() function?
Neither. It is locking on the monitor associated with the the object. In terms of the JVM we don't talk about an object's memory address because it is relocatable and it's not the hash code (even in terms of Object.hashcode()
) because that is not unique.
In terms of what you should be locking on, it should be the same final
object. Something like:
private final Object lockObject = new Object();
...
synchronized (lockObject) {
// do stuff that needed to be protected
}
You want it to be final
so that multiple threads can be guaranteed to be locking on the same object reference that is not changing. private
is good so outside classes can't screw up the locking inside of a class.
Here "asdf" has a unique hash but no unique memory address.
"asdf"
does not have a unique hash since other strings might have the same hash and it actually may have a unique "memory address" across all usage of "asdf" in your application if the compiler stores it in the Java string pool. That means that some completely different class might also have the same bad pattern code block and would affect the synchronization of your class because it would be locking on the same String
object instance. That's why a private
lock object is so important.
While we are on the subject, you must also never synchronize on a mutable value like a non-final object like Boolean
or Integer
. The following pattern is used often and is very wrong:
Boolean value = false;
...
// really bad idea
synchronized (value) {
if (value) {
value = false;
} else {
value = true;
}
}
This is very wrong because the value
reference is changing. So one thread might lock on it and then change it's reference value so another thread would lock on another object and both threads would be within the synchronized
at the same time. It is even worse because with a Boolean
there are only 2 values of true
and false
which are constants so multiple classes would then be locking on the same references.
You're synchronizing over an object, the memory address problem is purely an implementation one and doesn't concern you. As long as it's the same object (meaning the exact same instance), the synchronization is done.
If you use a different instance, the synchronization won't work. What you can do is define a public static constant as the lock :
public final static Object LOCK = new Object();
and use it
synchronized(SomeClass.LOCK) {