关于可见性的 Java 并发问题



我读到synchronized方法或块提供了两个功能:"互斥"和"可见性"。我想知道两件事。

public class A{
  private final Object lock = new Object();
  private C obj = new C();
  public void methodA(){
      synchronized(lock){
        obj.x = 1;
        obj.y=3;
       }
    }
public void methodB(C obj2){
          synchronized(lock){
           obj2.x = obj.x;
           }

}
}

假设我们有 2 个线程在 A 类型的全局共享对象上调用 methodA,并且lockthread1 获取,现在在thread1发布lock之后。 现在可见性是所有其他线程将读取对obj的更改? 即synchronized块中的每个更改都是可见的吗?还是我应该将 C 对象更改为 volatile 以使其更改对其他人可见?

制作obj volatile将使对 C 对象的引用volatile,即在同步块之外可见。它不会影响该对象的成员。

即,对 OBJ 的重新分配将对另一个线程可见。重新分配给其成员不会。

同步块内的每件事变化都是可见的吗?

是的,就是这个想法。JLS 17.45 定义发生在关系之前。特别:

在监视器上的每个后续锁定之前,都会在监视器上发生解锁。

因此,当 thread2 获取锁时,您可以保证它将在持有同一锁的同时看到 thread1 所做的更改。

我应该将 C 对象更改为易失性以使其对其他人可见吗?

volatile 保证如果你写: obj = new C();某处,后续读取obj会看到它现在引用一个新对象。但是,它不对obj的"内容"提供任何此类保证。因此,如果您编写:obj.x = someValue; ,并且 obj 是易失性的,则无法保证更改对另一个线程可见。除非你也x波动。

现在可见性是所有其他线程都将读取对 OBJ 的更改?

仅该锁的同步块内的线程。

同步块内的每件事变化都是可见的吗?

只是可能。为了保证可见性,线程必须位于同步块内。

或者我应该将 C 对象更改为易失性?

如果您没有同步,这将无济于事,并且在这里不会有任何区别。 volatile只会更改obj引用的行为,而不会更改其字段的行为。

为了回答您的问题,另一种方法可以简单地让您访问变量 obj,而无需锁定。您必须确保对 obj 的所有访问都必须通过锁小心保护,以便 obj 不会处于不一致的状态(由程序员定义)。

在您的特定示例中,它对我来说看起来足够一致。

不,访问obj字段仍然不是线程安全的。锁定对象上的同步仅允许您线程安全操作将值写入此块中的obj 的字段。

UPDobjvolatile对您没有帮助,因为您不会更改此字段自己的引用值。

最新更新