Почему поток ArrayList не сообщает характеристики CONCURRENT? - PullRequest
0 голосов
/ 06 мая 2019

При чтении документации к потокам Java я понимаю, что:

(new ArrayList<String>().stream().spliterator().characteristics() & Spliterator.CONCURRENT) != 0

Должен оценивать как истину. Однако при тестировании это не так.

Чего мне не хватает.

Java-документация, на которую я ссылаюсь: https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

Невмешательство

Потоки позволяют выполнять возможно параллельные агрегатные операции над различными источниками данных, включая даже не поточно-безопасные коллекции, такие как ArrayList. Это возможно, только если мы можем предотвратить вмешательство в источник данных во время выполнения потокового конвейера. За исключением iterator () и spliterator () операций escape-hatch, выполнение начинается, когда вызывается терминальная операция, и заканчивается, когда завершается терминальная операция. Для большинства источников данных предотвращение помех означает, что источник данных вообще не изменяется во время выполнения потокового конвейера. Заметным исключением из этого являются потоки, источники которых являются параллельными коллекциями, которые специально предназначены для обработки одновременных изменений. Источниками параллельных потоков являются те, чей Spliterator сообщает характеристику CONCURRENT. Соответственно, поведенческие параметры в потоковых конвейерах, источник которых может не совпадать, никогда не должны изменять источник данных потока. Говорят, что поведенческий параметр мешает несовместимому источнику данных, если он изменяет или вызывает изменение источника данных потока. Необходимость невмешательства распространяется на все трубопроводы, а не только на параллельные. Если источник потока не является одновременным, изменение источника данных потока во время выполнения потокового конвейера может вызвать исключения, неправильные ответы или несоответствующее поведение. Для потоковых источников с хорошим поведением источник может быть модифицирован до начала работы терминала, и эти модификации будут отражены в охватываемых элементах. Например, рассмотрим следующий код:

 List<String> l = new ArrayList(Arrays.asList("one", "two"));
 Stream<String> sl = l.stream();
 l.add("three");
 String s = sl.collect(joining(" "));

Сначала создается список, состоящий из двух строк: «одна»; и два". Затем создается поток из этого списка. Далее список модифицируется добавлением третьей строки: «три». Наконец элементы потока собираются и объединяются. Поскольку список был изменен до начала операции сбора терминала, результатом будет строка «один два три». Все потоки, возвращенные из коллекций JDK и большинства других классов JDK, хорошо себя ведут таким образом; о потоках, сгенерированных другими библиотеками, см. Построение низкоуровневого потока для требований к созданию потоков с хорошим поведением.

Я также пытался с CopyOnWriteArrayList. Там флаг CONCURRENT также не установлен. Например, для ConcurrentLinkedQueue установлен флаг CONCURRENT.

Версия

Я использую oracle javaSe-11 с jdk-11.0.2.

Ответы [ 2 ]

3 голосов
/ 06 мая 2019

CONCURRENT характеристика определяется как:

Характеристическое значение, означающее, что источник элемента может быть безопасно одновременно изменен (допускается добавление, замена и / или удаление) несколькими потоками без внешней синхронизации.

, тогда как документация ArrayList гласит:

Обратите внимание, что эта реализация не синхронизирована. Если несколько потоков одновременно обращаются к экземпляру ArrayList, и хотя бы один из потоков структурно изменяет список, он должен быть синхронизирован внешне. (Структурная модификация - это любая операция, которая добавляет или удаляет один или несколько элементов,…

Нельзя сказать более ясно, что ArrayList в качестве источника потока - это все, кроме CONCURRENT.

Документация, которую вы цитировали, не противоречит этому. Но я должен признать, что было бы намного лучше, если бы был разрыв абзаца перед «Для потоковых источников с хорошим поведением,…», чтобы дать читателю перерыв и отразить то, что было сказано до того, как это произойдет потом. другая точка, которая применяется к ArrayList, а также ко всем другим «источникам потоков с хорошим поведением», но больше не имеет отношения к характеристике CONCURRENT и не влияет на ограничения поведенческих параметров, описанные перед ней.

Все, что говорит новый пункт, заключается в том, что вы можете изменить ArrayList до работы терминала в терминале Stream, но все же только из одного потока (или с вашей собственной синхронизацией), но вы по-прежнему не разрешается выполнять структурные изменения во время текущей операции терминала, что исключает любые структурные изменения из поведенческих параметров, поскольку они всегда оцениваются во время текущей операции терминала.

Случай CopyOnWriteArrayList отличается, у него не будет характеристики CONCURRENT, потому что вместо этого его сплитераторы будут иметь характеристику IMMUTABLE, так как вы будете перебирать неизменный снимок; Модификации оригинального CopyOnWriteArrayList не повлияют на Stream.

0 голосов
/ 06 мая 2019

CopyOnWriteArrayList: Из названия видно, что для каждой записи делается свежая копия.ConcurrentLinkedQueue: параллельная запись разрешена с использованием массива объектов Lock

...