java - 字段的默认值是否保证在线程中可见?

标签 java multithreading final volatile

讨论this answer我想知道为什么我们在分配默认值时不使用同步。

class StateHolder {
    private int counter = 100;
    private boolean isActive = false;

    public synchronized void resetCounter() {
            counter = 0;
            isActive = true;
    }

    public synchronized void printStateWithLock() {
        System.out.println("Counter : " + counter);
        System.out.println("IsActive : " + isActive);
    }

    public void printStateWithNoLock() {
        System.out.println("Counter : " + counter);
        System.out.println("IsActive : " + isActive);
    }
}

该类看起来是线程安全的,因为对其字段的访问是由同步方法管理的。这样,我们所要做的就是安全地发布它。例如:

public final StateHolder stateHolder = new StateHolder();

它可以被视为安全出版物吗?我认为不,不能。咨询final field semantic (强调我的)我发现唯一可以保证的是 stateHolder 引用不是过时的。 :

A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

final 字段语义不关心 final 字段引用的对象的状态。这样,另一个线程也可能会看到字段的默认值。

问题:我们如何保证构造函数或实例初始值设定项中分配的字段值的内存一致性?

我认为我们必须将它们声明为 volatile 最终,因为没有happens-before分配引用和构造函数调用之间的关系。但许多库类并不以这种方式声明字段。 java.lang.String是一个例子:

public final class String 
          implements java.io.Serializable, Comparable<String>, CharSequence{
    //...
    private int hash; //neither final nor volatile
    //...
}

最佳答案

final 可以保证您在实例构造之后将看到实例变量的分配值,而无需任何进一步的操作。您只需要确保不会泄漏构造函数中构造的实例。

volatile 还可以保证您将看到为某些实例变量设置的默认值,因为实例变量初始值设定项保证在每个 JLS 12.5 Creation of New Class Instances 构造函数结束之前执行。

安全发布并非完全微不足道,但如果您坚持使用一种流行的机制来实现它,那么您应该完全没问题。您可以查看 Safe Publication and Safe Initialization in Java 了解一些更有趣的细节。

至于String.hash,它是所谓的良性数据竞争的一个流行示例。对 hash 实例变量的访问允许读取和写入竞争以及两次写入竞争。为了说明后者,两个线程可以同时:

  • 查看初始值0
  • 决定他们是第一个计算哈希值的人
  • 计算哈希码并写入同一变量,无需任何同步

由于两个原因,比赛仍然被允许并被认为是良性的:

  1. 不可变 String 实例的哈希码计算是幂等操作。
  2. 保证 32 位值的写入不会损坏。

不过,即使是良性的数据争用仍然不推荐。请参阅 Benign data races: what could possibly go wrong?Nondeterminism is unavoidable, but data races are pure evil

关于java - 字段的默认值是否保证在线程中可见?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36445294/

相关文章:

java - 如何获取线程内分配的变量的值?

java - 有没有办法提取哪个 JPanel 被单击?

java - 检测ActiveMQ流控

java - 考试样本,好奇我是否做对了

java - 如何监视文件夹中的更改?

Java Switch语句困惑

java - 关于Java中的静态final关键字

java - 如何在我的 swing 应用程序中使用 Java 类调用并向 Servlet 传递参数?

java - 有大量可供用户下载的可选数据。从哪里开始?

c++ - 在 C++ 中实现计时器/自动收报机类需要帮助