Java中同步的可见性影响 [英] Visibility effects of synchronization in Java

查看:78
本文介绍了Java中同步的可见性影响的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在此不兼容的代码示例中,Helper类变为不可变的 通过最终声明其字段. JMM保证不可变 在其他对象不可见之前,对象已完全构建 线. getHelper()方法中的块同步可确保 所有可以看到helper字段非空值的线程 还将看到完全初始化的Helper对象.

In this noncompliant code example, the Helper class is made immutable by declaring its fields final. The JMM guarantees that immutable objects are fully constructed before they become visible to any other thread. The block synchronization in the getHelper() method guarantees that all threads that can see a non-null value of the helper field will also see the fully initialized Helper object.

public final class Helper {
  private final int n;

  public Helper(int n) {
    this.n = n;
  }

  // Other fields and methods, all fields are final
}

final class Foo {
  private Helper helper = null;

  public Helper getHelper() {
    if (helper == null) {            // First read of helper
      synchronized (this) {
        if (helper == null) {        // Second read of helper
          helper = new Helper(42);
        }
      }
    }

    return helper;                   // Third read of helper
  }
}

但是,不能保证此代码在所有Java Virtual上都能成功 机器平台,因为没有事前发生的关系 在辅助程序的第一读和第三读之间.因此,这是 助手的三次读取有可能获得陈旧的空值 (可能是因为其值是由编译器缓存或重新排序的), 导致getHelper()方法返回空指针.

However, this code is not guaranteed to succeed on all Java Virtual Machine platforms because there is no happens-before relationship between the first read and third read of helper. Consequently, it is possible for the third read of helper to obtain a stale null value (perhaps because its value was cached or reordered by the compiler), causing the getHelper() method to return a null pointer.

我不知道该怎么做.我可以同意,在第一读和第三读之间的关系之前没有发生任何事情,至少没有立即关系.在某种意义上说,第一读必须在第二之前发生,并且第二读必须在第三之前发生,因此第一读必须在第三之前发生

I don't know what to make of it. I can agree that there is no happens before relationship between first and third read, at least no immediate relationship. Isn't there a transitive happens-before relationship in a sense that first read must happen before second, and that second read has to happen before third, therefore first read has to happen before third

有人能更熟练地进行阐述吗?

Could someone elaborate more proficiently?

推荐答案

不,没有传递关系.

JMM背后的想法是定义JVM必须遵守的规则.只要JVM遵循这些规则,就可以根据需要授权它们重新排序和执行代码.

The idea behind the JMM is to define rules that JVM must respect. Providing the JVM follows these rules, they are authorized to reorder and execute code as they want.

在您的示例中,第2次读取和第3次读取不相关-例如,通过使用synchronizedvolatile不会引入存储障碍.因此,允许JVM执行以下操作:

In your example, the 2nd read and the 3rd read are not related - no memory barrier introduced by the use of synchronized or volatile for example. Thus, the JVM is allowed to execute it as follow:

 public Helper getHelper() {
    final Helper toReturn = helper;  // "3rd" read, reading null
    if (helper == null) {            // First read of helper
      synchronized (this) {
        if (helper == null) {        // Second read of helper
          helper = new Helper(42);
        }
      }
    }

    return toReturn; // Returning null
  }

您的呼叫将返回一个空值.但是,将创建一个单例值.但是,随后的调用仍可能会得到一个空值.

Your call would then return a null value. Yet, a singleton value would have been created. However, sub-sequent calls may still get a null value.

如所建议的,使用易失性会引入新的存储障碍.另一个常见的解决方案是捕获读取的值并将其返回.

As suggested, using a volatile would introduce new memory barrier. Another common solution is to capture the read value and return it.

 public Helper getHelper() {
    Helper singleton = helper;
    if (singleton == null) {
      synchronized (this) {
        singleton = helper;
        if (singleton == null) {
          singleton = new Helper(42);
          helper = singleton;
        }
      }
    }

    return singleton;
  }

由于您依赖局部变量,因此无需重新排序.一切都在同一线程中发生.

As your rely on a local variable, there is nothing to reorder. Everything is happening in the same thread.

这篇关于Java中同步的可见性影响的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆