views:

141

answers:

1

Yes, the private member variable bar should be final right? But actually, in this instance, it is an atomic operation to simply read the value of an int. So is this technically thread safe?

class Foo {
    private int bar;
    public Foo(int bar) {
        this.bar = bar;
    }
    public int getBar() {
        return bar;
    }
}

// assume infinite number of threads repeatedly calling getBar on the same instance of Foo.

EDIT:

Assume that this is all of the code for the Foo class; any threads with a reference to a Foo instance will not be able to change the value of bar (without going to such lengths as using reflection etc.)

+5  A: 

Final update: so my first conclusion happened to be right, just my reasoning was faulty :-( I re-edited my answer to make it somewhat coherent, not to hide the traces of my earlier blunder.

Conclusion

As @Wyzard pointed out, even though there is no way to change bar after construction, Foo is still not thread safe. The problem is not atomicity but visibility. If thread 1 is changing the value of bar in the constructor (from its default value of 0), there is no guarantee when other threads will get to see the new value (or whether they see it at all).

So foo looks like an immutable object. Quoting from Java Concurrency in Practice, section 3.4:

An object is immutable if:

  • Its state cannot be modified after construction;
  • All its fields are final; and
  • It is properly constructed (the this reference does not escape during construction).

Foo looks OK on 1) and 3), but not 2). And that is a crucial point, due to the reasoning above. Declaring a variable final is one way of ensuring its visibility between different threads. The other means are declaring bar volatile, or synchronizing its access method(s). But of course, in case of an immutable object, neither of these would make much sense.

Final Fields

So why do finalfields guarantee visibility? Answer from Java Concurrency in Practice, section 3.5.2:

Because immutable objects are so important, the JavaMemory Model offers a special guarantee of initialization safety for sharing immutable objects. As we've seen, that an object reference becomes visible to another thread does not necessarily mean that the state of that object is visible to the consuming thread. In order to guarantee a consistent view of the object's state, synchronization is needed.

Immutable objects, on the other hand, can be safely accessed even when synchronization is not used to publish the object reference. For this guarantee of initialization safety to hold, all of the requirements for immutability must be met: unmodi-fiable state, all fields are final, and proper construction. [...]

Immutable objects can be used safely by any thread without additional synchronization, even when synchronization is not used to publish them.

This guarantee extends to the values of all final fields of properly constructed objects; final fields can be safely accessed without additional synchronization. However, if final fields refer to mutable objects, synchronization is still required to access the state of the objects they refer to.

And what happens if the field is not final? Other threads may silently see a stale value of the field. There is no exception or any kind of warning - that is one reason why these kinds of bugs are so difficult to trace.

Péter Török
In what way is it not thread safe though, what are the consequences in this situation? I am trying to gain a deeper understanding of the mechanics of concurrency in Java. I know all the different ways I could make this thread safe.
Finbarr
@Peter, assume that the code displayed along with the comment is the only code in the system. The only way to modify the value of the `int` would be through reflection.
Finbarr
@Finbarr, it's not a matter of the int being modified after construction. You should read about [cache coherence](http://en.wikipedia.org/wiki/Cache_coherence). In short, on a multiprocessor system, when one processor writes to memory, the change may not be immediately visible to other processors unless synchronization is done. The result is that even after thread 1 has finished running the constructor, the other threads might still see the old contents of that memory address, from before the constructor ran, for awhile.
Wyzard
@Wyzard, very good point, I missed that one!
Péter Török
What I don't really get is in a situation like this, what is the upshot of the variable not being final? Is there a possibility that the JVM will throw an Exception of some sort if two threads try to access the variable at exactly the same nanosecond or what?
Finbarr
@Peter @Wyzard thanks for the information. This is something that has been puzzling me for some time. Infact, this is something that all Java programmers should be made more aware of as it is very important to note.
Finbarr
@Finbarr, see my update regarding final variables.
Péter Török
@Peter it's a shame I can't upvote your question twice. Thanks again.
Finbarr
@Finbarr, it is more the merit of Mr. Goetz et al, authors of JCiP :-) Strongly recommended reading!
Péter Török
@Peter It is actually on my reading list for the class Distributed Programming at university, so perhaps I will buy it!
Finbarr
Bit late to this one, but worth checking out §17.5 of the JLS (see http://java.sun.com/docs/books/jls/third_edition/html/memory.html#66562) which makes this extremely explicit -- see the 'FinalFieldExample' in the 'Discussion' section, which points out explicitly that the read of a non-final field, set in the constructor to 4, MAY be read as the default value (0). This is as explicit as you're going to get.
Cowan