在 java 中对对象进行同步,然后更改已同步的变量的值

我遇到了这样的代码

synchronized(obj) {

   obj = new Object();

}

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


答案 1

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

除非您有充分的理由不这样做,否则您可能希望在最终对象上进行同步(为了可见性的缘故)。在这种情况下,您可能希望使用单独的锁变量。例如:

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

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

答案 2

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

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

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

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

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

1) 计算表达式,然后

2) 获取表达式计算结果到的值的锁,然后

3) 执行块。

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

此代码已损坏。别这样。锁定不会改变的东西。


推荐