如果一个对象不再发生变化,它是否可以安全地跨线程共享?或者换句话说:如果 var(非最终)字段实际上是不可变的,它们是否可以安全发布?
带有伪(抱歉)代码的人为示例:
class Root {
var field1: Int = 0
var field2: String = ""
var field3: SomeOtherObject = null
// many more fields here
}
class SomeOtherObject {
var field1: String = ""
var field2: YetAnotherObject = null
}
var root = new Root()
root.field1 = 123
root.field2 = "some string"
root.field3 = new SomeOtherObject()
root.field3.field1 = "data"
root.field3.field2 = new YetAnotherObject()
// ... So basically an object tree of arbitrary levels of nesting
// From here on out none of the fields will change
// now if other threads use the above data, e.g.
Future {
println(root.field1)
}
new Thread() {
override def run() = root.field3.field1 ++ "hello"
}.start()
// In the real project it will be a thread from a webserver getting passed the object tree root
我意识到这不是很地道的 Scala。假设我实际上无法访问创建对象树的代码。
据我所知这段代码不是线程安全的。没有任何数据是安全发布的。没有一个字段是最终的 (val) 并且看不到同步原语(除了 Thread.start()
和 Future.apply
中的任何内容。
我不是在问如何让它成为线程安全的。我的问题是:如果可变数据在通过共享数据的引用后永远不会改变,那么共享可变数据是否是线程安全的?我不相信 JMM/jsr133 可以保证非最终字段的这一点。
但是我不能制作一个测试程序来证明这段代码已经损坏并且需要更多的同步。在 Java5 添加更强的保证之后,让我相信 JMM 中的某些东西?我在 Java8 上使用 Scala 2.12。
我找到了这个 all fields are final以及jep188 ,但这些只是建议。任何人都可以指出为什么上面的代码似乎是安全的规则/规范吗?
最佳答案
If an object is not mutated anymore, can it be safely shared across threads? Or in other words: are var (non-final) fields safely published if they are practically immutable?
没有。
您需要创建先行关系以保证对字段的写入发生在读取之前。这不会自动发生。
它可能看起来 可以工作,因为您运行代码的特定 JVM 正在将写入刷新到主内存。但是,这并不能保证。这是并发错误的有害之处:明显没有问题并不表示正确。
为了让评论中的讨论更持久一些:
Would it be different if all fields were assigned to in the constructor? (Still not final fields). Would a happens-before relationship be created if the root reference is held in an AtomicReference? As in, would that then apply to 'the whole object tree' if that makes sense?
如果在构造函数中赋值它们肯定不会有帮助:如果字段是最终的,您只会得到 happens-before(赋值发生在构造函数结束之前)。
在使用
AtomicReference
方面,这取决于您何时分配它。 "The memory effects for accesses and updates of atomics generally follow the rules for volatiles" ,所以如果在分配字段后设置AtomicReference
,你会得到一个 happens-before ;但是如果您在设置AtomicReference
之后设置字段,则不会得到 happens-before。
关于java - 如果一个对象不再发生变化,它是否可以安全地跨线程共享? (斯卡拉/ java ),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61749202/