title: What implementation detail makes this code fail so easily?
body: <p>This question is not about the well-known and documented fact that <code>HashMap</code> is not thread-safe, but about its specific failure modes on HotSpot and JDK code. I am surprised by how readily this code fails with an NPE:</p>
  
  <pre><code>public static void main(String[] args) {
      Map&lt;Integer, Integer&gt; m = new HashMap&lt;&gt;(0, 0.75f);
      IntStream.range(0, 5).parallel().peek(i -&gt; m.put(i, i)).map(m::get).count();
  }
  </code></pre>
  
  <p>There is no mystery as to where the NPE comes from: in the <code>.map(m::get)</code> step while trying to unbox a <code>null</code>. It fails in about 4 out of 5 runs.</p>
  
  <p>On my machine <code>Runtime#availableProcessors()</code> reports 8, so presumably the range of length 5 is split into 5 subtasks, each with just a single member. I also assume my code runs in interpreted mode. It might be calling into JIT-compiled <code>HashMap</code> or <code>Stream</code> methods, but the top level is interpreted, therefore precluding any variations where <code>HashMap</code> state is loaded into thread-local memory (registers/stack), thus delaying the observation of updates by another thread. If some of the five <code>put</code> operations don't execute literally during the same time on different cores, I don't expect it to destroy the <code>HashMap</code>s internal structure. The timing of individual tasks must be extremely precise given the little amount of work. </p>
  
  <p>Is it really the precise timing (<code>commonPool</code>'s threads must be unparked), or is there another route to cause this to fail on Oracle/OpenJDK HotSpot? My current version is </p>
  
  <pre><code>java version "1.8.0_72"
  Java(TM) SE Runtime Environment (build 1.8.0_72-b15)
  Java HotSpot(TM) 64-Bit Server VM (build 25.72-b15, mixed mode)
  </code></pre>
  
  <p>UPDATE: I find that even making just <em>two</em> insertions has a similarly high failure rate:</p>
  
  <pre><code>IntStream.range(0, 2).parallel().peek(i -&gt; m.put(i, i)).map(m::get).count();
  </code></pre>