Как работает поток Java, когда одновременно работает список, в котором он работает? - PullRequest
0 голосов
/ 13 ноября 2018

Если у меня есть список из 10 элементов, и я использую его в виде потока, отфильтруйте его по возрасту и соберите в виде списка. И между ними кто-то добавляет 5 элементов в список. Каким будет поведение потоков это будет работать только с 10 элементами или 15 ??.

Ответы [ 3 ]

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

Прочитайте раздел «Невмешательство» в javadoc .В частности:

Если источник потока не является параллельным, изменение источника данных потока во время выполнения потокового конвейера может вызвать исключения, неправильные ответы или несоответствующее поведение.

Таким образом, ответ на ваш вопрос заключается в том, что он зависит от типа списка, является ли он параллельным или нет, и, если это не так, как он обрабатывает параллельные операции, и в какой момент вы делаете модификацию (до или всередина терминала операции).

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

Ну, я попробовал с ArrayList и Vector , и оба из них дали мне ConcurrentModificationException .

Однако CopyOnWriteArrayList работает нормально.

    int valueLength = 10;
    List<Integer> myList = new Vector<>();
    for (int i = 0; i < valueLength; i++) {
        myList.add(i);
    }
    new Thread(() -> {
        try {
            myList.stream().filter(i -> {
                try {
                    if (i % 3 == 0) {
                        Thread.sleep(10);
                        return false;
                    }
                    return true;
                } catch (Exception e) {
                    e.printStackTrace();
                    return false;
                }
            }).collect(Collectors.toList());
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }).start();

    new Thread(() -> {
        try {
            for (int i = valueLength; i > 0; i--) {
                if (i % 3 == 0) {
                    Thread.sleep(5);
                    myList.remove(i);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
0 голосов
/ 13 ноября 2018

Вы получите ConcurrentModificationException - если вам повезет. Из документации об исключении наиболее релевантная часть:

Как правило, результаты итерации в этих обстоятельствах не определены. Некоторые реализации Iterator (включая реализации всех реализаций коллекций общего назначения, предоставляемых JRE) могут выбрасывать это исключение, если обнаруживается такое поведение. Итераторы, которые делают это, известны как итераторы с быстрым отказом, так как они быстро и чисто терпят неудачу, а не рискуют произвольным, недетерминированным поведением в неопределенное время в будущем.

Я попробовал этот пример кода:

List<Integer> myList = new ArrayList<>();
myList.add(1);
myList.add(2);
myList.add(3);
Thread threadA = new Thread(() -> {
    try {
        myList.add(4);
        Thread.sleep(1000);
        myList.add(5);
        Thread.sleep(1000);
        myList.add(6);
        Thread.sleep(1000);
        myList.add(7);
        Thread.sleep(1000);
        myList.add(8);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
Thread threadB = new Thread(() -> {
    myList.forEach(i -> {
        try {
            System.out.println(i);
            Thread.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
});

threadA.start();
threadB.start();

Что приводит к:

1
2
Exception in thread "Thread-1" java.util.ConcurrentModificationException
    at java.util.ArrayList.forEach(ArrayList.java:1252)
    at com.company.Main.lambda$main$2(Main.java:30)
    at java.lang.Thread.run(Thread.java:748)

Отключение не-поточно-безопасного ArrayList для синхронизированного Vector результата:

1
2
3
4

Но вы не можете полагаться на это поведение. В документации совершенно ясно, что она не определена, и это может привести к изменению между различными реализациями или даже разными версиями. Не делай этого. Используйте потокобезопасные контейнеры, такие как Vector и ConcurrentHashMap, и используйте примитивы синхронизации вокруг них таким образом, чтобы иметь смысл для проблемы, которую вы пытаетесь решить.

...