[concurrency-interest] Does JDK 9 String.hashCode() have a bug?

Jonas Konrad me at yawk.at
Wed Sep 28 07:55:19 EDT 2016


Take this example:

class Race {
     String s = "a";
     void writer() { s = "b"; }

     void reader() {
         String tmp = s;
         assert s.hashCode() == s.hashCode();
     }
}

class String {
     final char[] value; int hash;

     int hashCode() {
         int h = hash;
         if (h == 0) {
             hash = h = computeHashCode(value);
         }
         return h;
     }
}

In the JMM, can't that assertion fail? Since the 'hash' field is not
final and the String is not safely published, The reader could observe
the 'old' hashCode of "a" on the first read, not do any computation
because h != 0, and then see the 'new' hashCode with h == 0 in the
second read.

I see how the two reads are a good example of your other
"NonBenignRace2" though.

Thanks,
- Jonas

On 09/28/2016 01:37 PM, Aleksey Shipilev wrote:
> On 09/28/2016 01:28 PM, Jonas Konrad wrote:
>> On 09/28/2016 12:38 PM, Aleksey Shipilev wrote:
>>> P.S. Your explanation is similar to a more detailed of mine here:
>>>
>>> https://shipilev.net/blog/2016/close-encounters-of-jmm-kind/#wishful-benign-is-resilient
>>
>> Wouldn't, according to that section in your blog, the old hashcode (and
>> the suggested fix) also be unsafe?
>
> Why unsafe? JDK 8 String.hashCode is a classic example of the benign
> data race. It's benignity comes, as with other benign data races, from
> two conditions:
>
>  a) The object being published is safely constructed. In this particular
> case, we publish the primitive value, and it is safe by definition.
>
>  b) We read the field only once, which is exactly what JDK 8
> String.hashCode does, but JDK 9 String.hashCode lacks.
>
> JDK 8:
>
>   public int hashCode() {
>     int h = hash;  // <--- read once
>     if (h == 0) {  // <--- not 0? proceed to return
>       for (char v : value) {
>         h = 31 * h + v;
>       }
>       if (h != 0) {
>         hash = h;
>       }
>     }
>     return h;  // <--- return the verified value, DO NOT racy read again
>   }
>
>> From what I can tell the hashcode in Java 8 already assumed (which it
>> can because it's JDK code) that the VM inserts a memory barrier in
>> the String constructor because of the final value field.
>
> Note this is *within* the String instance already, no "final value field
> init in String" applies here.
>
> Thanks,
> -Aleksey
>
>
>
>


More information about the Concurrency-interest mailing list