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

我遇到了这样的代码

synchronized(obj) { obj = new Object(); } 

有些事情对此感觉不对,我无法解释,这段代码是好的还是有什么问题,请指出来。 谢谢

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

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

 class Foo { private final Object lock = new Object(); private Object obj; public void method() { synchronized(lock) { obj = new Object(); } } } 

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

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

如果obj是一个局部变量,并且没有其他线程正在评估它以获取它的锁定,如此处所示则无关紧要。 否则会严重破坏,以下情况适用:

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

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

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

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

1)然后评估表达式

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

3)执行块。

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

这段代码坏了。 不要这样做。 锁定不会改变的事物。

这是一种不常见的用法,但似乎在相同的场景中有效。 我在JmDNS的代码库中找到的一个:

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

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