Jason
Jason

Reputation: 13986

How does Java Synchronized compare locked objects?

I want to lock an object over multiple API requests such that only one request per user can enter a block of code.

Does synchronized(obj) lock based on the object's reference or its hashCode() function?

I.e. could I do:

synchronized("asdf") {
    doSomethingNifty();
}

Here "asdf" has a unique hash but no unique reference.

Upvotes: 7

Views: 2801

Answers (3)

Gray
Gray

Reputation: 116908

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.

Upvotes: 12

Stochastically
Stochastically

Reputation: 7846

The lock is on the instance of object itself, which you can think of as the memory location if you want to think about it like that. So what you're suggesting won't work.

Instead, it sounds like you just need to have a single object corresponding to the block of code that you want to protect. So somewhere in the setup process for your program you need something like

static Object lockObject = new Object();

and then you can do

synchronized(lockObject) {
    doSomethingNifty();
}

However, if doSomethingNifty() relates to a particular object, then it would make a lot of sense to use that object instead of lockObject. Also make sure that doSomethingNifty() is fast to execute, otherwise there'll be a lot of users left waiting.

Upvotes: 0

Denys Séguret
Denys Séguret

Reputation: 382354

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) {

Upvotes: 2

Related Questions