I don't think this is correct. 

First of all, it's not really relevant if String is immutable or not, in 
this case: It could already break at the (non-final, non-volatile) field x, 
which could be null if an instance of P was unsafely published. Of course, 
String being immutable has the implications you say - it doesn't require 
safe publishing and will still show up "all-or-nothing" - but I don't think 
this is relevant here. 

However, in this case, the instance of `P` **is** safely published: Just 
like a volatile field store, storing into a CHM is a safe publication. So 
the code cannot fail - if the .get returns not-null, the String field will 
also be non-null (assuming it has been set to non-null before the put of 
course, which it has here). This is basically a textbook JCIP case of 
safely publishing a mutable object. 

It looks like the example mistakenly does `new String` instead of `new P`, 
so that might have been a bit confusing :D 

- Jonas 

Am Samstag, 17. November 2018 08:52:53 UTC+1 schrieb Gil Tene:
>
> Well, "yes, sort of".
>
> Your example works for String, because it is immutable.
>
> Specifically, there is a happens-before relationship between the 
> constructor's initialization of the final fields of the String and the 
> subsequent publishing store of the result of the constructor.
>
> However, non-final fields have no such happens-before relationship with 
> the publishing store. E.g. the cached hash field in String 
> <https://github.com/bpupadhyaya/openjdk-8/blob/master/jdk/src/share/classes/java/lang/String.java#L117>
>  may 
> or may not be have been initialized when p.x is read. 
>
> This [race on hash field initialization] has no visible side effect 
> because of how the hash field is used in String: it caches the hash code, 
> and will freshly compute the hash from the immutable char[] if the field 
> holds a 0. So even if the initialization races with a call to hashCode() 
> <https://github.com/bpupadhyaya/openjdk-8/blob/master/jdk/src/share/classes/java/lang/String.java#L1452>
>  [e.g. 
> a call to hashcode happens on another thread before the hash field is 
> initialized, and the initialization overwrites the cached value with a 0], 
> all that would happen is a recomputation of the same hash. The value 
> returned by hashCode() won't change.
>
> But in other cases where non-final fields are involved, e.g. if p.x was a 
> Foo with a non-final field y with a getter, p.x.getY() may return an 
> uninitialized value after the get() returns a non-null p.
>
> On Friday, November 16, 2018 at 3:59:47 AM UTC-8, John Hening wrote:
>>
>> Hello, let's look at:
>>
>>
>>
>>     class P {      public String x;
>>     }
>>     
>>     ConcurrentHashMap<Integer, P> x = new ConcurrentHashMap<>();
>>
>>     new Thread(() -> {                 // Thread 1
>>         x.put(1, new String("x"));     // 1
>>     }).start();
>>     
>>     new Thread(() -> {                 // Thread 2 
>>             P p = x.get(1);             // 2
>>             if(p != null){             
>>                 print(p.x);            // 4
>>             }
>>     }).start();
>>
>>
>>
>>
>> If thread 2 observes p != null is it guaranteed by JMM that p.x is 
>> initialized? For my eye, yes, because:
>>
>> Let's assume a such execution when p != null was true. It means that 
>> there is a synchronize-with relation between x.put(1, new String("x")); 
>> --sw--> x.get().
>> Putting and getting an element from ConcurrentHashMap contain 
>> synchronization access (and, actually, synchronization-with is between 
>> them). 
>>
>> In a result, there is a chain of happens-before relation:
>>
>>     tmp = new String("x") --hb--> x.put(1, tmp) --hb--> x.get(1) --hb--> 
>> read(p.x)
>>
>>
>>
>>
>> Yes?
>>
>>
>>
>>
>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"mechanical-sympathy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mechanical-sympathy+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to