3

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?

1 Answer 1

5

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?

No.

You need to create a happens-before relationship to guarantee that the writes to the fields happen before reads. This doesn't happen automatically.

It might appear to work because the particular JVM you run the code on is flushing the writes to main memory. However, it's not guaranteed. This is the pernicious thing about concurrency bugs: the apparent lack of a problem doesn't indicate correctness.


To make the discussion in comments a bit more permanent:

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?

  • It certainly wouldn't help if they are assigned in the constructor: you only get the happens-before (assignment happens before end of constructor) if the fields are final.

  • In terms of using AtomicReference, it depends on when you assign it. "The memory effects for accesses and updates of atomics generally follow the rules for volatiles", so you would get a happens-before if you set the AtomicReference after you've assigned the fields; but you don't get the happens-before if you set the fields after setting the AtomicReference.

3
  • Ok, that was what I was thinking. 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?
    – Wtr
    Commented May 12, 2020 at 10:48
  • 2
    @Wtr it certainly wouldn't help if they are assigned in the ctor, you only get the HB if the fields are final. Commented May 12, 2020 at 10:49
  • 1
    @Wtr in terms of using AtomicReference, it depends on when you assign it. "The memory effects for accesses and updates of atomics generally follow the rules for volatiles", so you would get a happens-before if you set the AR after you've assigned the fields; but you don't get the HB if you set the fields after setting the AR. Commented May 12, 2020 at 10:55

Not the answer you're looking for? Browse other questions tagged or ask your own question.