我遇到了像这样的代码
synchronized(obj) {
obj = new Object();
}
对此我感觉不对,我无法解释,这段代码是否正常或者其中有什么问题,请指出。 感谢
答案 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;
}
它的作用是在返回的列表上进行同步,以便其他人不会修改此列表,然后复制此列表。在这种特殊情况下,只需要对原始对象进行锁定。