Этот вопрос связан не с известным и документированным фактом, что HashMap не является потокобезопасным, а касается конкретных режимов отказа в коде HotSpot и JDK. Я удивлен тем, насколько с легкостью этот код выходит из строя с NPE:
public static void main(String[] args) {
Map<Integer, Integer> m = new HashMap<>(0, 0.75f);
IntStream.range(0, 5).parallel().peek(i -> m.put(i, i)).map(m::get).count();
}
Нет никакой тайны относительно того, откуда приходит NPE: на шаге .map(m::get) при попытке распаковать a null. Он не работает примерно в 4 из 5 запусков.
На моей машине Runtime#availableProcessors() сообщает 8, поэтому, предположительно, диапазон длины 5 разбит на 5 подзадач, каждый из которых имеет только один член. Я также предполагаю, что мой код работает в интерпретируемом режиме. Это может быть вызов JIT-скомпилированных методов HashMap или Stream, но верхний уровень интерпретируется, поэтому исключает любые изменения, в которых состояние HashMap загружается в локальную память потока (регистры/стек), тем самым задерживая наблюдение обновлений другого потока. Если некоторые из пяти операций put не выполняются буквально в одно и то же время на разных ядрах, я не ожидаю, что он разрушит внутреннюю структуру HashMap. Сроки отдельных задач должны быть чрезвычайно точными, учитывая небольшой объем работы.
Точно ли точное время (commonPool потоки должны быть неактивными), или есть другой маршрут, который может привести к сбою в Oracle/OpenJDK HotSpot? Моя текущая версия
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)
ОБНОВЛЕНИЕ: я считаю, что даже создание только двух вставк имеет такую же высокую частоту отказа:
IntStream.range(0, 2).parallel().peek(i -> m.put(i, i)).map(m::get).count();