构建后可见的Java非final int

我有一个带有非final int变量的java类,我在构造函数中将其显式初始化为0.对该变量的所有其他访问都由ReentrantLock管理。 我是否必须担心线程不会看到0的初始值,因为我没有在构造函数中使用锁?

是的,你必须担心。 为避免在这种情况下出现问题,您需要安全发布对象引用。

来自Java Concurrency in Practice :

要安全地发布对象,必须同时使对象的引用和对象的状态对其他线程可见。 正确构造的对象可以通过以下方式安全发布:

  • 从静态初始化程序初始化对象引用;
  • 将对它的引用存储到volatile字段或AtomicReference中;
  • 将对它的引用存储到正确构造的对象的最终字段中; 要么
  • 将对它的引用存储到由锁正确保护的字段中。

在其他情况下,您可以(理论上)面对在构造函数调用完成之前new结果可用于其他线程的情况(由于可能的操作重新排序)。

但请注意,如果0是默认值而不是构造函数中写入的值,则保证它是可见的( JLS§17.4.4 ):

  • 向每个变量写入默认值(零,false或null)与每个线程中的第一个操作同步。 虽然在分配包含变量的对象之前向变量写入默认值似乎有点奇怪,但从概念上讲,每个对象都是在程序开始时使用其默认初始值创建的

来自Java Concurrency in Practice:

必须安全地发布不是不可变的对象,这通常需要发布和使用线程同步。

仅通过不在构造函数中发布其引用来安全地发布对象。 即构造函数不强制执行必要的before-before关系。 因此,即使您没有在其构造函数中发布对象引用,您仍然可能会遇到并发问题。 有关详细信息和示例,请参阅本书中的相关章节 。

为了做出安全的出版物,作者提出了以下方法:

要安全地发布对象,必须同时使对象的引用和对象的状态对其他线程可见。 正确构造的对象可以通过以下方式安全发布:

从静态初始化程序初始化对象引用;

将对它的引用存储到volatile字段或AtomicReference中;

将对它的引用存储到正确构造的对象的最终字段中; 要么

将对它的引用存储到由锁正确保护的字段中。 本质上,必须在对象的构造和另一个线程对该对象的访问之间引入适当的“先发生”关系。

正如作者所指出的那样,通过线程安全集合传递的对象也是安全发布的(例如,通过LinkedBlockingQueue等通过工作线程传递的项目)。

确实,将值存储到原始int字段(但不是像long那样的64位字段)是primefaces的,这意味着即使以非线程安全的方式从不同的方式访问该字段,您也无法观察到“奇怪”的值。线。 但是当一个物体尚未正确构建时,可能会发生其他不好的事情(说实话,我不知道究竟会发生什么,但它肯定不值得一试)。

总而言之,您需要安全地发布对象,此时将值正确设置为0并正确实例化对象。