Есть ли в JDK Java параллельный список?


277

Как я могу создать параллельный экземпляр List, где я могу получить доступ к элементам по индексу? Есть ли в JDK классы или фабричные методы, которые я могу использовать?


28
Почему не конструктивно? Несколько предложенных CopyOnWriteArrayList, который не найден в .Net. Вы можете сказать, что оба вопроса относятся друг к другу, но не закрывать этот !!!
Алик Эльзин-килака

1
Я понятия не имею, почему Джаррод Роберсон решил, что было бы неплохо взять подробные правки, сделанные Стефаном, и вернуть их обратно к первоначальному, плохо сформулированному вопросу. Ответ Джаррода все еще вполне приемлем. Фактически, CopyOnWriteArrayList - единственный параллельный класс, реализующий List в JDK. Озадаченный ...
Мэтт Пасселл

10
Поскольку принятый ответ на первоначальный вопрос и Стефан поставил совершенно несвязанный вопрос с кучей исходного кода , что оригинальный плакат не включает в любом месте меняющегося вопроса полностью, вызвавшие больше ответов , которые были предполагающих другие вещи , чем Listкакой оригинал специально говорит это требование, которое считается вандализмом. Модератор уже заблокировал вопрос из-за людей, которые жалуются на то, что ответы не отвечают на эту разрушенную версию вопроса.

1
/ locked/ closed/ Предыдущий комментарий

8
Нет причин для закрытия этого вопроса. Он спрашивает о классах в JDK, который совсем не похож на поиск библиотеки; это основа Java.
Maaartinus

Ответы:


175

В java.util.concurrent есть параллельная реализация списка . CopyOnWriteArrayList в частности.


84
Обратите внимание, что он копирует весь список на каждую вставку, поэтому он часто неэффективен.
dfrankow

22
@dfrankow Но это может еще более эффективным , если вы итерацию гораздо больше , чем вы обновляете.
b1nary.atr0phy

Не работает, как показано здесь. У меня есть исключения, хотя я просто использую метод addAll и читаю его с помощью потока. stackoverflow.com/questions/1527519/…
devssh

166

Если вы не заботитесь о доступе на основе индекса и просто хотите сохранить в List характеристики сохранения порядка вставки, вы можете рассмотреть java.util.concurrent.ConcurrentLinkedQueue . Поскольку он реализует Iterable, после добавления всех элементов вы можете перебирать содержимое, используя расширенный синтаксис for:

Queue<String> globalQueue = new ConcurrentLinkedQueue<String>();

//Multiple threads can safely call globalQueue.add()...

for (String href : globalQueue) {
    //do something with href
}

4
Я думаю, что упрощенный оператор for ( :) называется foreach: docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
AlikElzin-kilaka

2
@ AlikElzin-kilaka Ты прав. Я думаю, что это имя всегда беспокоило меня, потому что фактический синтаксис не включает слово «каждый», но я обновлю ответ, чтобы использовать официальное имя. :)
Мэтт Пасселл

3
@ AlikElzin-kilaka Nitpicking, но в соответствии с версией 8 JLS это называется «расширенный для утверждения». То же самое в руководстве по Java .
Роланд

1
@ Роланд определенно НЕ придирается. Существует (сейчас) разница между «для каждого» и «улучшенным для» в Java.
hfontanez

2
@Roland косвенно. Я считаю, что они переименовали «для каждого цикла» в «улучшенный для», чтобы устранить путаницу между Stream.forEach и тем, что сейчас известно как расширенный для.
hfontanez

128

Вы можете очень хорошо использовать Collections.synchronizedList (List), если все, что вам нужно, это простая синхронизация вызовов:

 List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

70
Результат synchronizedList"синхронизирован", но не "параллелен". Одна фундаментальная проблема заключается в том, что многие операции List, основанные на индексах, сами по себе не являются атомарными и должны быть частью более широкой конструкции взаимного исключения.

6
ИМО, asing a Vectorявляется более простым, чем Collections.synchronizedList(new ArrayList<Object>()).
Стефан

8
Результат synchronizedList является "синхронизированным", но не "параллельным".
Канагавелу Сугамар

46

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

Сама идея параллельной коллекции состоит в том, что каждая отдельная операция является атомарной и может выполняться без явной блокировки / синхронизации.

Следовательно, получение элемента в позиции nиз заданной Listв качестве атомарной операции не имеет особого смысла в ситуации, когда ожидается одновременный доступ.


6
Йоахим, я думаю, ты ударился ногтем по голове. Возьмем, к примеру, список только для чтения в качестве параллельного списка. Получение элемента в позиции N из списка не только имеет смысл, но и является суть проблемы. Таким образом, неизменный список (нижний регистр L) будет хорошим примером, но это не список (верхний регистр L). CopyOnWriteArrayList работает одновременно, но многим людям не нравится производительность. Решение по линии веревок (веревок), вероятно, будет хорошим победителем.
Johnstosh

1
Очень хороший момент. Но список, который использует ОП, может иметь очень специфическое применение. Например, он может быть заполнен в параллельной среде, затем «заблокирован» (что бы это ни значило) и затем безопасно доступен по индексу. Таким образом, на первом этапе заполнения такого списка все еще потребуется поточно-ориентированная реализация. К сожалению, ОП не уточнил, как будет использоваться список, который он ищет.
igor.zh

13

У вас есть эти варианты:

  • Collections.synchronizedList()Вы можете обернуть любую Listреализацию ( ArrayList, LinkedListили список третьих сторон). Доступ к каждому методу (чтение и запись) будет защищен с помощью synchronized. При использовании iterator()или улучшении цикла for вы должны вручную синхронизироваться; во время итерации другие потоки полностью блокируются даже для чтения. Вы также можете синхронизировать отдельно для каждого hasNextи nextзвонков, но тогда ConcurrentModificationExceptionэто возможно.

  • CopyOnWriteArrayList: это дорого модифицировать, но читать без ожидания. Итераторы никогда не генерируют ConcurrentModificationException, они возвращают снимок списка в момент создания итератора, даже если список изменяется другим потоком во время итерации. Полезно для нечасто обновляемых списков. Массовые операции, такие как addAll, предпочтительнее для обновлений - внутренний массив копируется реже.

  • Vector: очень нравится synchronizedList, но итерация тоже синхронизирована. Однако итераторы могут выдавать ConcurrentModificationException, если вектор модифицируется другим потоком во время итерации.

Другие варианты:

  • Collections.unmodifiableList(): без блокировки, поточно-ориентированный, но не модифицируемый
  • Queueили Dequeможет быть альтернативой, если вы добавляете / удаляете только в конце списка и повторяете список. Нет доступа по индексу и нет добавления / удаления в произвольных местах. У них есть несколько одновременных реализаций с лучшей производительностью и лучшим параллельным доступом, но это выходит за рамки этого вопроса. Вы также можете взглянуть на JCTools , они содержат более производительные реализации очереди, предназначенные для одного потребителя или одного производителя.

4

введите описание изображения здесь

CopyOnWriteArrayList - это потокобезопасный вариант ArrayList, в котором все мутативные операции (добавление, установка и т. Д.) Реализуются путем создания новой копии базового массива.

CopyOnWriteArrayList - это параллельная альтернатива синхронизированного List, реализующего интерфейс List и его часть пакета java.util.concurrent и его потокобезопасную коллекцию.

public class CopyOnWriteArrayList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

CopyOnWriteArrayList является отказоустойчивым и не генерирует исключение ConcurrentModificationException, когда базовый CopyOnWriteArrayList изменяется во время итерации, используя отдельную копию ArrayList.

Это обычно слишком дорого, потому что массив копий, включающий каждую операцию обновления, будет клонированной копией. CopyOnWriteArrayList - лучший выбор только для частых операций чтения.

/**
         * Returns a shallow copy of this list.  (The elements themselves
         * are not copied.)
         *
         * @return a clone of this list
         */
        public Object clone() {
            try {
                @SuppressWarnings("unchecked")
                CopyOnWriteArrayList<E> clone =
                    (CopyOnWriteArrayList<E>) super.clone();
                clone.resetLock();
                return clone;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError();
            }
        }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.