Java非最终int在构造后可见

Java非最终int在构造后可见,java,concurrency,Java,Concurrency,我有一个java类,它带有一个非final int变量,我在构造函数中将该变量显式初始化为0。对变量的所有其他访问都由ReentrantLock管理。我是否必须担心线程不会看到初始值0,因为我没有在构造函数中使用锁?是的,您必须担心。为了避免出现这种情况下的问题,您需要安全地发布对象引用 发件人: 要安全地发布对象,对对象的引用和对象的状态都必须对其他用户可见 线程同时运行。正确构造的对象可以通过以下方式安全发布: 从静态初始值设定项初始化对象引用 将对它的引用存储到易失性字段或原子引用中 将

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

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

发件人:

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

  • 从静态初始值设定项初始化对象引用
  • 将对它的引用存储到易失性字段或原子引用中
  • 将对它的引用存储到正确构造的对象的最终字段中;或
  • 将对它的引用存储到由锁正确保护的字段中
在其他情况下,您(理论上)可以面对这样的情况:在构造函数调用完成之前,
new
的结果将可供其他线程使用(由于可能的操作重新排序)

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

  • 将默认值(零、假或空)写入每个变量会同步- 每个线程中的第一个操作。虽然看起来有点 在包含 变量,从概念上讲,每个对象都是在 具有默认初始化值的程序

从实践中的Java并发性:

不可变的对象必须是 安全出版,通常 需要由两个 发布和消费线程

仅通过不在构造函数中发布其引用,对象就不能安全地发布。也就是说,构造器没有强制执行必要的先发生后关系。因此,即使不在其构造函数中发布对象引用,也可能存在并发性问题。有关详细信息和示例,请参见本书中的

为了安全出版,作者建议以下方法:

要安全地发布对象,两个 对对象和对象的引用 必须使对象的状态对用户可见 同时使用其他线程。A. 正确构造的对象可以 安全出版人:

从初始化对象引用 静态初始化器

将对它的引用存储到 挥发性场或原子参考

将对它的引用存储到最终 一个正确构造的 对象或

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

正如作者所指出的,通过threadsafe集合传递的对象也可以安全发布(例如,通过LinkedBlockingQueue通过工作线程传递的项等)

的确,将值存储到基本
int
字段(而不是像
long
这样的64位字段)是原子的,这意味着即使您从不同的线程以非线程安全的方式访问该字段,也无法观察到“奇怪”的值。但是,当一个对象还没有正确构造时,可能会发生其他不好的事情(老实说,我不知道到底会发生什么,但肯定不值得一试)


总之,您需要安全地发布对象,此时该值被正确设置为0,并且对象被正确实例化

理论上,你有一个更糟糕的问题。(非安全发布)其他线程在写入和/或读取变量后可能会看到分配。尽管很容易修复,甚至允许不安全的发布。只需删除赋值并使用初始值即可。代码更少,更安全。在“敏感情况”中,通常最好使默认值安全。因此在我的例子中,我将一个基本int设置为0。因此int的默认值也是0。是否有可能在构造对象后,另一个线程可以看到0以外的值。@richs:在
0
的情况下,这应该不是问题。@axtavt我想我现在担心的是,其他线程在执行读写操作后可能会看到赋值。它已经推出,但我必须在下一个推出周期修复它。thanks@richs:您说过其他访问由
ReentrantLock
保护。如果是,这不是问题。@axtavt因此,如果线程a构造对象,它可以在构造后发布0(默认值)。然后线程B读取该值(由锁保护),并将其修改为1。线程B修改该值后,是否可以发布构造函数中赋值的值0。