Чи є спільний список у JDK Java?


277

Як я можу створити екземпляр паралельного списку, де я можу отримати доступ до елементів за індексом? Чи є в JDK якісь класи чи заводські методи, які я можу використовувати?


28
Чому б не конструктивним? Кілька запропонованих CopyOnWriteArrayList, які не знайдені в .Net. Ви можете сказати, що обидва питання стосуються одне одного, але не закривати це !!!
AlikElzin-kilaka

1
Я не маю поняття, чому Джаррод Роберсон вважає, що було б гарною ідеєю взяти детальні редакції, зроблені Стефаном, і повернути їх до оригінального, погано сформульованого питання. Відповідь Джаррода все ще є цілком прийнятною. Насправді CopyOnWriteArrayList - єдиний паралельний клас, що реалізує Список у 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

Якщо вам не байдуже мати доступ на основі індексу та просто хочете, щоб збереження порядку вставки в Список, ви можете розглянути java.util.concurrent.ConcurrentLinkedQueue . Оскільки він реалізує Iterable, після того, як ви закінчите додавати всі елементи, ви можете перебирати вміст, використовуючи розширений для синтаксису:

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

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

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

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

2
@ AlikElzin-kilaka Ви маєте рацію. Я думаю, що це ім'я мене завжди турбувало, оскільки власне синтаксис не включає слово "кожен", але я оновлю відповідь, щоб використовувати офіційну назву. :)
Метт Пассел

3
@ AlikElzin-kilaka Nitpicking, але згідно з версією JLS 8 його називають "розширеним для твердження". Те саме в навчальному посібнику java .
Роланд

1
@Roland, безумовно, НЕ запорошений. Існує (зараз) різниця між "для кожного" та "розширеним для" на Java.
hfontanez

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

128

Ви можете дуже добре використовувати Collections.synchronizedList (Список), якщо все, що вам потрібно, - це проста синхронізація виклику:

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

70
Результат synchronizedList"синхронізований", але не "паралельний". Одне основне питання, що багато операцій Списку - на основі індексу - самі по собі не є атомними і повинні бути частиною більшого конструктиву взаємного виключення.

6
IMO, асингa Vectorє більш простим, а не простим Collections.synchronizedList(new ArrayList<Object>()).
Стефан

8
Результатом синхронізованого списку є "синхронізований", але не "паралельний".
Канагавелу Сугумар

46

Оскільки акт придбання позиції та отримання елемента із заданої позиції, природно, вимагає певного блокування (у списку не може бути структурних змін між цими двома операціями).

Сама ідея паралельної колекції полягає в тому, що кожна операція сама по собі є атомною і може бути виконана без явного блокування / синхронізації.

Тому отримання елемента в позиції nзаданої Listяк атомної операції не має занадто великого сенсу в ситуації, коли передбачається паралельний доступ.


6
Йоахіме, я думаю, ти вдарив цвях по голові. Візьмемо для прикладу список для читання як паралельний список. Отримати елемент у позиції N зі списку має не лише сенс, але це і є головним завданням проблеми. Отже, непорушний список (нижній регістр L) був би хорошим прикладом, але це не Список (верхній регістр L). CopyOnWriteArrayList є одночасною, але багатьом не подобається виконання. Рішення по лінії канатів (канатні канати), мабуть, буде хорошим переможцем.
Джонстош

1
Дуже хороший момент. Але у списку, який буде використовуватись, ОП може мати дуже специфічне використання. Наприклад, він може бути заповнений у одночасному середовищі, потім "заблокований" (що б це не означало), а потім безпечно отримати доступ за індексом. Отже, на першій фазі заповнення такого Списку все одно буде потрібно безпечна реалізація. На жаль, ОП не була конкретна щодо того, як буде використовуватися Список, який він шукає.
igor.ж

13

У вас є такі варіанти:

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

  • CopyOnWriteArrayList: це дорого змінювати, але читати не дочекавшись. Ітератори ніколи не кидають ConcurrentModificationException, вони повертають знімок списку в момент створення ітератора, навіть якщо список змінюється іншим потоком під час ітерації. Корисно для нечасто оновлених списків. Масові операції, як-от addAll, бажані для оновлень - внутрішній масив копіюється рідше.

  • Vector: дуже подобається synchronizedList, але ітерація теж синхронізована. Однак ітератори можуть кидати ConcurrentModificationException, якщо вектор модифікується іншим потоком під час ітерації.

Інші варіанти:

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

4

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

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

CopyOnWriteArrayList - це паралельна альтернатива синхронізованого списку реалізацій інтерфейсу списку та його частини пакету java.util.concurrentі його колекції, захищеної від потоків.

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

CopyOnWriteArrayList не захищений відмов і не кидає ConcurrentModificationException, коли під CoteOnWriteArrayList змінюється під час ітерації окрема копія 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.