在java中同步对象,然后更改synchronized-on变量的值

时间:2011-07-15 18:51:56

标签: java concurrency synchronization

我遇到了像这样的代码

synchronized(obj) {

   obj = new Object();

}

对此我感觉不对,我无法解释,这段代码是否正常或者其中有什么问题,请指出。 感谢

4 个答案:

答案 0 :(得分:20)

这可能不是你想要做的。您正在同步一个您不再持有引用的对象。考虑另一个运行此方法的线程:在更新obj的引用以指向新对象后,它们可能会进入并尝试锁定锁定。此时,它们在与第一个线程不同的对象上进行同步。这可能不是你所期待的。

除非你有充分的理由不这样做,否则你可能想要在最终的Object上进行同步(为了可见性)。在这种情况下,你可能想要使用一个单独的锁变量。例如:

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

    public void method()
    {
        synchronized(lock)
        {
            obj = new Object();
        }
    }
}

答案 1 :(得分:3)

有人可能认为他们正在做的事情是好的,但可能不是他们想要的。在这种情况下,您正在同步obj变量中的当前值。创建新实例并将其放入obj变量后,锁定条件将发生变化。如果这就是在这个块中发生的一切,它可能会起作用 - 但是如果它之后做了其他事情,那么该对象将无法正确同步。

最好安全地在包含对象上或在另一个互斥锁上完全同步。

答案 2 :(得分:2)

如果obj是一个局部变量,并且没有其他线程正在评估它以获取它as shown here的锁,那么它并不重要。否则会严重破坏以下情况:

(发布这个是因为其他答案的措辞不够 - “可能”在这里还不够 - 并且没有足够的细节。)

每次线程遇到同步块时, 在获得锁定之前,它必须通过评估synchronized关键字后面的parens中的表达式来确定需要锁定的对象。

如果在线程计算此表达式后更新引用,则线程无法知道。它将继续获取它之前识别为锁的旧对象的锁定。最终它进入旧对象的同步块锁定,而另一个线程(在锁更改后尝试进入块)现在将锁定为新对象并进入持有新锁的同一对象的同一块,而且你没有相互排斥。

JLS中的相关部分是14.19。执行synchronized语句的线程:

1)评估表达式,然后

2)获取表达式求值的值的锁定,然后

3)执行块。

在成功获取锁定时,它不会再次重新审视评估步骤。

此代码已损坏。不要这样做。锁定不会改变的事物。

答案 3 :(得分:0)

这是一种不常见的用法,但似乎在相同的场景中有效。我在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;
}

它的作用是在返回的列表上进行同步,以便其他人不会修改此列表,然后复制此列表。在这种特殊情况下,只需要对原始对象进行锁定。