Synchronizing on an object in java, then changing the value of the synchronized-on variable
Asked Answered
B

4

24

I came across a code like this

synchronized(obj) {

   obj = new Object();

}

Something does not feel right about this , I am unable to explain, Is this piece of code OK or there is something really wrong in it, please point it out. Thanks

Baynebridge answered 15/7, 2011 at 18:51 Comment(1)
You should only lock on final fields to avoid ever doing this. It's a bad idea.Lanfri
T
21

It's probably not what you want to do. You're synchronizing on an object that you're no longer holding a reference to. Consider another thread running this method: they may enter and try to hit the lock at the moment after the reference to obj has been updated to point to the new object. At that point, they're synchronizing on a different object than the first thread. This is probably not what you're expecting.

Unless you have a good reason not to, you probably want to synchronize on a final Object (for visibility's sake.) In this case, you would probably want to use a separate lock variable. For example:

class Foo
{
    private final Object lock = new Object();
    private Object obj;

    public void method()
    {
        synchronized(lock)
        {
            obj = new Object();
        }
    }
}
Thebaine answered 15/7, 2011 at 18:59 Comment(1)
javaroots.com/2015/01/… what would be the output of this code ?Intuitive
M
5

If obj is a local variable and no other thread is evaluating it in order to acquire a lock on it as shown here then it doesn't matter. Otherwise this is badly broken and the following applies:

(Posting this because the other answers are not strongly-worded enough --"probably" is not sufficient here -- and do not have enough detail.)

Every time a thread encounters a synchronized block, before it can acquire the lock, it has to figure out what object it needs to lock on, by evaluating the expression in parens following the synchronized keyword.

If the reference is updated after the thread evaluates this expression, the thread has no way of knowing that. It will proceed to acquire the lock on the old object that it identified as the lock before. Eventually it enters the synchronized block locking on the old object, while another thread (that tries to enter the block after the lock changed) now evaluates the lock as being the new object and enters the same block of the same object holding the new lock, and you have no mutual exclusion.

The relevant section in the JLS is 14.19. The thread executing the synchronized statement:

1) evaluates the expression, then

2) acquires the lock on the value that the expression evaluates to, then

3) executes the block.

It doesn't revisit the evaluation step again at the time it successfully acquires the lock.

This code is broken. Don't do this. Lock on things that don't change.

Mangrum answered 4/5, 2017 at 15:4 Comment(1)
idk why this answer doesn't have high votes. It answers the question and explains why locking on an old object is dangerous.Shaunda
O
3

This is a case where someone might think what they are doing is OK, but it probably isn't what they intended. In this case, you are synchronizing on the current value in the obj variable. Once you create a new instance and place it in the obj variable, the lock conditions will change. If that is all that is occurring in this block, it will probably work - but if it is doing anything else afterwards, the object will not be properly synchronized.

Better to be safe and synchronize on the containing object, or on another mutex entirely.

Obliquely answered 15/7, 2011 at 18:54 Comment(0)
H
2

It's a uncommon usage but seems to be of valid in same scenarios. One I found in the codebase of JmDNS:

public Collection<? extends DNSEntry> getDNSEntryList(String name) {
    Collection<? extends DNSEntry> entryList = this._getDNSEntryList(name);
    if (entryList != null) {
        synchronized (entryList) {
            entryList = new ArrayList<DNSEntry>(entryList);
        }
    } else {
        entryList = Collections.emptyList();
    }
    return entryList;
}

What it does is to synchonize on the returned list so this list does not get modified by others and then makes a copy of this list. In this special situation the lock is only needed for the original object.

Haeres answered 4/5, 2017 at 16:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.