"Java Memory Model" in "Chapter 2 Concurrency on the JVM and the Java Memory Model" of "Learning Concurrent Programming in Scala" states that
If an object contains only final fields and the reference to the enclosing object does not become visible to another thread before the constructor completes, then the object is considered immutable and can be shared between the threads without any synchronization.
This sounds logical. A final field will not have any mutation and thus there is no data races to worry about that object monitors should be called upon to avoid.
The author then provides an example involving closure
var inc: () => Unit = null
val t = thread { if (inc != null) inc() }
private var number = 1
inc = () => { number += 1 }
The local number variable is captured by the lambda, so it needs to be lifted. The statement in the last line translates to an anonymous Function0 class instantiation:
number = new IntRef(1) // captured local variables become objects
inc = new Function0 {
val $number = number // recall - vals are final!
def apply() = $number.elem += 1
}
There is no happens-before relationship between the assignment to inc and the read of inc by the thread t. However, if the t thread sees that inc is not null, invoking inc still works correctly, because the $number field is appropriately initialized since it is stored as a field in the immutable lambda object.
What I am trying to understand:
- Does inc != null ensure that all of the variables referenced by Function0 object will be properly initialized? Which JMM property ensures that since t is not in happens-before relationship with number? Or is it more of property of the object which ensure that it is accessible only after it is properly initialized?
- What happens if number is set to 2 before t thread evaluates inc? Does t evaluate 3? $number is an object reference and even though it is a val, the value can change even though reference stays the same.