Kevin Krumwiede
Kevin Krumwiede

Reputation: 10308

Java final field heresy: am I reasoning correctly?

I recently started learning C# and went straight for the memory model. C# and Java have similar (though perhaps not identical) thread safety guarantees regarding reads and writes of volatile fields. But unlike writes to final fields in Java, writes to readonly fields in C# do not provide any specific thread safety guarantee. Thinking about how thread safety works in C# led me to doubt whether there is any real advantage to final fields behaving the way they do in Java.

I learned about the supposed importance of final three years ago. I asked this question and got a detailed answer that I accepted. But now I think it's wrong, or at least irrelevant. I still think fields should be final whenever possible, just not for the reasons commonly believed.

The value of a final field is guaranteed to be visible to any other thread after the constructor returns. But the reference to the object itself must be published in a thread safe manner. If the reference is published safely, then the visibility guarantee of final becomes redundant.

I considered the possibility that it has something to do with public static fields. But logically, a class loader must synchronize the initialization of a class. Synchronization makes the thread safety of final redundant.

So I'm putting forth the heretical idea that the only real value of final is to make immutability self-documenting and self-enforcing. In practice, private non-final fields (and in particular, array elements) are perfectly thread safe as long as they are not modified after the constructor returns.

Am I wrong?

Edit: Paraphrasing section 3.5 of Java Concurrency in Practice,

Two things can go wrong with improperly published objects. Other threads could see a stale value for the reference, and thus see a null reference or other older value even though a value has been set. But far worse, other threads could see an up-to-date value for the reference, but stale values for the state of the object.

I understand how final fields solve the second problem, but not the first problem. The highest voted answer so far argues that the first problem is not a problem.

Edit 2: This question arises from a confusion of terminology.

Like the asker of a similar question, I've always understood the term "safe publication" to mean that both an object's internal state and the reference to the object itself are guaranteed visible to other threads. In favor of this definition, Effective Java cites Goetz06, 3.5.3 in defining "safe publication" as (emphasis added)

Transferring such an object reference from one thread to others

Also in favor of this definition, note that the section of Java Concurrency in Practice paraphrased above refers to potentially stale references as being "improperly published."

Whatever you call it, I didn't think that unsafely publishing a reference to an immutable object could ever be useful. But according to this answer, it can. (The example given there is a primitive value, but the same principle could apply to reference values.)

Upvotes: 0

Views: 166

Answers (2)

Eugene
Eugene

Reputation: 120988

I've read your question a couple of times and still have some issues understating it. I will just try to augment the other answer - which is in fact correct. final in Java is all about re-orderings (or happens-before as you call it).

First this is guaranteed by the JLS, see Final Field Semantics. In the example you can see that a single final field is guaranteed to be correctly seen by other threads, while the other is not. The JLS is correct, but under the current implementation a single field to be final would be enough - read further.

Every single write to a final field inside a constructor is followed by two memory barriers - StoreStore and LoadStore (thus the name of happens-before); because the store to a final field will happen before read to the same field - guaranteed via memory barriers.

But the current implementation does not do that - meaning that the memory barriers do not happen after every single write to a final - they happen at the end of the constructor, see this. You will see this line that is important:

 _exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final());

Op_MemBarRelease being the actual LoadStore|StoreStore barrier, thus under the current implementation it is enough to have a single field to be final for all others to be safely published as well. But, of course, do that at your own risk.

volatile in this context would not make publication enough - since it would simply not introduce the necessary barriers, you can read a bit more here.

Note that the reference publishing is not a concern, simply because the JLS says: Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values..

Upvotes: 0

shmosel
shmosel

Reputation: 50746

But the reference to the object itself must be published in a thread safe manner. If the reference is published safely, then the visibility guarantee of final becomes redundant.

The first sentence is wrong; the second is therefore irrelevant. final may be redundant in the presence of other safe publication techniques, like synchronization or volatile. But the point of immutable objects is that they're inherently thread-safe, meaning they'll be seen in a consistent state regardless of how the reference is published. So you don't need those other techniques in the first place, at least as far as safe publication is concerned.

EDIT: OP correctly points out that there's some ambiguity around the term "safe publication". In this context I'm referring to consistency of the object's internal state. The issue of visibility as it affects the reference is, in my opinion, a valid but separate concern.

Upvotes: 4

Related Questions