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?
A contrived example with pseudo (sorry) code:
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
I realise this is not very idiomatic Scala. Let's imagine I don't actually have access to the code creating the object tree.
As far as I know this code is not thread safe. None of the data is safely published. None of the fields are final (val) and there's no synchronisation primitive in sight (besides Thread.start()
and whatever is inside Future.apply
.
I'm not asking how to make it thread-safe. My question is: is it thread-safe to share mutable data if it won't ever change after passing the reference through which that data can be reached is shared? I don't believe the JMM/jsr133 guarantees this for non-final fields.
However I cannot make a test program that proves this code is broken and needs more synchronisation. Leading me to believe something in the JMM after Java5 added a stronger guarantee? I'm using Scala 2.12 on Java8.
I've found this all fields are final as well as jep188, but those are only proposals. Can anyone point me to the rule/specification why above code seems to be safe?