Есть ли в Collection.stream () внутренняя синхронизация? - PullRequest
0 голосов
/ 12 октября 2018

Я пытался воспроизвести (и решить) ConcurrentModificationException, когда экземпляр HashMap читается и пишется несколькими Thread с.

Отказ от ответственности: я знаю, что HashMapне поддерживает потоки.

В следующем коде:

import java.util.*;

public class MyClass {

    public static void main(String args[]) throws Exception {
        java.util.Map<String, Integer> oops = new java.util.HashMap<>();
        oops.put("1", 1);
        oops.put("2", 2);
        oops.put("3", 3);

        Runnable read = () -> {
            System.out.println("Entered read thread");

            /*
             * ConcurrentModificationException possibly occurs
             *
            for (int i = 0; i < 100; i++) {
                List<Integer> numbers = new ArrayList<>();
                numbers.addAll(oops.values());
                System.out.println("Size " + numbers.size());
            }
            */

            for (int i = 0; i < 100; i++) {
                List<Integer> numbers = new ArrayList<>();
                numbers.addAll(oops.values()
                        .stream()
                        .collect(java.util.stream.Collectors.toList()));
                System.out.println("Size " + numbers.size());
            }
        };

        Runnable write = () -> {
            System.out.println("Entered write thread");
            for (int i = 0; i < 100; i++) {
                System.out.println("Put " + i);
                oops.put(Integer.toString(i), i);
            }
        };

        Thread writeThread = new Thread(write, "write-thread");
        Thread readThread = new Thread(read, "read-thread");

        readThread.start();
        writeThread.start();

        readThread.join();
        writeThread.join();
    }
}

По сути, я делаю два потока: один продолжает помещать элементы в HashMap, другой выполняет итерации на HashMap.values().

В потоке read, если я использую numbers.addAll(oops.values()), случайным образом возникает ConcurrentModificationException.Хотя строки печатаются случайным образом, как и ожидалось.

Но если я переключусь на numbers.addAll(oops.values().stream().., я не получу никакой ошибки.Однако я наблюдал странное явление.Все строки в потоке read печатаются после в строках, напечатанных в нити write.

Мой вопрос: Collection.stream() имеет как-то внутреннюю синхронизацию?

ОБНОВЛЕНИЕ :

Используя JDoodle https://www.jdoodle.com/a/IYy,, похоже на JDK9 и JDK10, я получу ConcurrentModificationException, как и ожидалось.

Спасибо!

Ответы [ 3 ]

0 голосов
/ 12 октября 2018

Я быстро просмотрел исходный код Java 8, он выдает ConcurrentModificationException.Метод HashMap values() возвращает подкласс AbstractCollection, метод которого spliterator() возвращает ValueSpliterator, который выбрасывает ConcurrentModificationException.

Для информации Collection.stream() использует сплитератор для прохожденияили разделить элементы источника.

0 голосов
/ 13 октября 2018

То, что вы видите, совершенно случайно;имейте в виду, что внутренне System.out.println делает synchronzied;таким образом, может быть , что как-то заставляет его выглядеть так, как будто результаты выглядят по порядку.

Я не слишком углубился в ваш код - потому что анализирую, почему HashMap, который не является поточно-безопасным,мисс ведет себя, скорее всего, бесполезно;как известно, документально подтверждено, что он не является потокобезопасным.

Что касается ConcurrentModificationException, то в документации конкретно говорится, что он попытается в лучшем случае бросить это;так что либо java-8 был слабее в этом пункте, либо это снова случайно.

0 голосов
/ 12 октября 2018

Мне удалось получить ConcurrentModificationException с потоками на Java 8, но с некоторыми изменениями в коде: увеличено количество итераций и количество добавленных элементов для сопоставления в отдельном потоке со 100 до 10000. А также добавлено CyclicBarrier, поэтомучто циклы в потоках чтения и записи запускаются более или менее одновременно.Я также проверил исходный код сплитератора для Hashmap.values(), и он выдает ConcurrentModificationException, если были внесены некоторые изменения в карту.

if (m.modCount != mc) //modCount is number of modifications mc is expected modifications count which is stored before trying to fetch next element
                throw new ConcurrentModificationException();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...