assylias assylias - 3 months ago 15
Java Question

Immutability and reordering

Comment on accepted answer

This question has generated much more heat than I woul dhave imagined. An important conclusion I drew from public and private discussions with members of the concurrency interest mailing list (i.e. people who actually work on implementing those things):

If you can find a sequentially consistent reordering that does not break any inter-thread happens-before relationship, it is a valid reordering (i.e. is compliant with the program order rule and causality requirement).

That's what John Vint has provided in his answer.




Original question

The code below (Java Concurrency in Practice listing 16.3) is not thread safe for obvious reasons:

public class UnsafeLazyInitialization {
private static Resource resource;

public static Resource getInstance() {
if (resource == null)
resource = new Resource(); // unsafe publication
return resource;
}
}


However, a few pages later, in section 16.3, they state:


UnsafeLazyInitialization
is actually safe if
Resource
is immutable.


I don't understand that statement:


  • if
    Resource
    is immutable, any thread observing the
    resource
    variable will either see it null or fully constructed (thanks to the strong guarantees of final fields provided by the Java Memory Model)

  • however, nothing prevents instruction reordering: in particular the two reads of
    resource
    could be reordered (there is one read in the
    if
    and one in the
    return
    ). So a thread could see a non null
    resource
    in the
    if
    condition but return a null reference (*).



I think
UnsafeLazyInitialization.getInstance()
can return null even if
Resource
is immutable. Is it the case and why (or why not)?


Note: I expect an argumented answer rather than pure yes or no statements.




(*) to better understand my point about reordering, this blog post by Jeremy Manson, who is one of the authors of the Chapter 17 of the JLS on concurrency, explains how String's hashcode is safely published via a benign data race and how removing the use of a local variable can lead to hashcode incorrectly returning 0, due to a possible reordering very similar to what I describe above:


What I've done here is to add an additional read: the second read of hash, before the return. As odd as it sounds, and as unlikely as it is to happen, the first read can return the correctly computed hash value, and the second read can return 0! This is allowed under the memory model because the model allows extensive reordering of operations. The second read can actually be moved, in your code, so that your processor does it before the first!

Answer

The confusion I think you have here is what the author meant by safe publication. He was referring to the safe publication of a non-null Resource, but you seem to get that.

Your question is interesting - is it possible to return a null cached value of resource?

Yes.

The compiler is allowed to reorder the operation like such

public static Resource getInstance(){
   Resource reordered = resource;
   if(resource != null){
       return reordered;
   }
   return (resource = new Resource());
} 

This doesn't violate the rule of sequential consistency but can return a null value.

Whether or not this is the best implementation is up for debate but there is no rules to prevent this type of reordering.

Comments