Проблемы с двумя потоками, обращающимися к часто обновляемому Arraylist - PullRequest
5 голосов
/ 11 апреля 2011

У меня есть ArrayLists, которые хранят много объектов, и объекты часто добавляются и удаляются из ArrayLists.Один поток работает со структурами данных и обновляет объекты ArrayList каждые 20 мс или около того.Другой поток пересекает ArrayLists и использует их элементы для рисования объектов (также каждые 20-30 мс).

Если списки ArrayLists пересекаются с использованием цикла for, IndexOutOfBoundsExceptions в изобилии.Если ArrayLists просматриваются с использованием итераторов, ConcurrentModificationExceptions в изобилии.Синхронизация списков массивов выглядит следующим образом:

<code>
List list = Collections.synchronizedList(new ArrayList());
synchronized(list) {
//use iterator for traversals
}

Не создает исключений, но существенно снижает производительность.Есть ли способ обойти эти ArrayList без исключения с помощью throw и без потери производительности?

СПАСИБО!

Ответы [ 6 ]

4 голосов
/ 11 апреля 2011

Хороший подход к этой проблеме - заставить потоки работать с разными копиями списка. Тем не менее, CopyOnWriteArrayList здесь не подходит, так как он создает копию при каждой модификации, но в вашем случае было бы лучше создавать копии реже.

Итак, вы можете реализовать это вручную: первый поток создает копию обновленного списка и публикует ее через переменную volatile, второй поток работает с этой копией (я предполагаю, что первый поток изменяет только список, а не объекты в нем):

private volatile List publicList;

// Thread A
List originalList = ...;
while (true) {
    modifyList(originalList); // Modify list
    publicList = new ArrayList(originalList); // Pusblish a copy
}

// Thread B
while (true) {
    for (Object o: publicList) { // Iterate over a published copy
        ...
    }
}
2 голосов
/ 11 апреля 2011

А как насчет копирования ArrayList в новую переменную до его итерации? Таким образом, вам нужно только синхронизировать блок копирования, а не всю итерацию списка.

2 голосов
/ 11 апреля 2011

Вы пытались использовать Iterator и использовать CopyOnWriteArrayList ?Гарантированно не выбрасывать ConcurrentModificationException с.

Из Oracle Javadocs (выделение выделено):

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

Обычно это слишком дорого, , но может быть более эффективным, чем альтернативы, когда операции обхода значительно превосходят число мутаций, и полезно, когда вы не можете или не хотите синхронизировать обходы., однако необходимо исключить помехи между параллельными потоками.Метод итератора в стиле «снимок» использует ссылку на состояние массива в момент создания итератора.Этот массив никогда не изменяется во время жизни итератора, поэтому вмешательство невозможно, и итератор гарантированно не выдает исключение ConcurrentModificationException .Итератор не будет отражать добавления, удаления или изменения в списке с момента его создания.Операции изменения элементов на самих итераторах (удаление, установка и добавление) не поддерживаются.Эти методы генерируют исключение UnsupportedOperationException.

1 голос
/ 11 апреля 2011

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

Если ваш шаблон доступа только через итератор и вы не делаете произвольный доступ, тогда используйте очередьможет быть лучшим выбором, так как вы можете использовать такие вещи, как ConcurrentLinkedQueue.См., Например, JAVA: контроль параллелизма для доступа к списку в java .

1 голос
/ 11 апреля 2011

Вы можете использовать CopyOnWriteArrayList, который не получает исключение ConcurrentModificationException и не требует синхронизации, или вы можете сделать что-то вроде.

List list = Collections.synchronizedList(new ArrayList());

List copy;
// lock the list for the minimal amount of time.
synchronized(list) {
    copy = new ArrayList(list);
}
// use the copy of the array list.

BTW CopyOnWriteArrayList будет выглядеть как

List list = new CopyOnWriteArrayList();

// use the list.
1 голос
/ 11 апреля 2011

Класс CopyOnWriteArrayList был создан для решения этой проблемы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...