2016-12-29 2 views
1

Я хочу иметь синхронизированную коллекцию (список или набор), которая будет потокобезопасной. Моя коллекция будет содержать объекты класса Variable, которые имеют имена, такие как имя, значение и т. Д. Теперь скажем, что два потока хотят изменить объекты из этого списка (добавить новые или удалить существующие). Какое лучшее решение для этого? Я нашел что-то вроде этого в stackoverflow.Синхронизированная коллекция переменных

List<Variable> varList = Collections.synchronizedList(new ArrayList<Variable>()); 

void processList(List<Variable> varList, String name) 
{ 
    synchronized(varList) { 
     varList.stream().filter(o -> o.getName().equals(name)).findFirst().ifPresent(o -> o.setValue(100)); 
    // or 
    varList.remove(0); 
    // or 
    varList.add(new Variable()); 
    } 
} 

Будет ли это работать без проблем? Или есть лучшие решения?

+0

Если потоки изменяют объекты 'Variable' в списке, то класс' Variable' должен быть потокобезопасным, а не списком. –

+0

Это работает, список будет потокобезопасным. –

+0

Если переменная Variable.class является изменяемой, то у вас есть общее состояние на этих объектах, что может привести к другому «состоянию гонки». –

ответ

2

Лучшее решение зависит от варианта использования. Если вам нужна упорядоченная коллекция случайных совпадений, то есть 3 варианта.

1) Тот, который вы описали (Synchronized List). Он будет работать так же быстро, как подкладочный тип Collection, с drawBack, что вся коллекция будет заблокирована для Thread acces.

Кроме того, реализация Итератора по умолчанию не является Threadsafe и поэтому требует дополнительных мер threadSafty. (Иетатор часто используется в forEach, clear и других реализациях по умолчанию (java 8)).

2)CopyOnWriteArrayList. Это единственный ThreadSafe (включая Iterator с функцией моментального снимка) randomAcces List в JDK.

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

3)Vector. Это немного медленнее на действия «читать», связанные с CopyOnWriteList, хотя механизм «записи» не требует создания копии данных резервного копирования.

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



Sumarry:

Synchronized List

  • Высокая производительность с низким числом экранных нитей.
  • Итератор не Потокобезопасная

CopyOnWriteArrayList

  • Быстрая производительность «читать» действиям
  • копии данных аккомпанемента на действия «записи» (могут быть проблемы с большими Collections)
  • Итератор защищен резьбой (с использованием функции моментального снимка)

Vector

  • Более высокая производительность (compaired в список синхронизированного) с большим количеством экранного Threads.
  • Итератор не Потокобезопасная
+0

Спасибо за ответ. В моем случае производительность не так важна, поэтому я буду часто оставаться со Списком. Но, похоже, у меня есть еще несколько проблем: 1. Я хочу изменить/изменить объекты внутри этого списка (изменить их поля, а не только удалять/добавлять новые элементы). Согласно другим комментариям по моему вопросу, мне нужна дополнительная защита? Как именно я могу это сделать? 2. Вы сказали, что операции итератора arent thread safe для List. Таким образом, поиск объекта с использованием методов stream() и filter() не является хорошей идеей? 3. Мне действительно нужен синхронизированный список, или я должен просто поместить «обычный» список в синхронизированный блок? –

+1

1) Синхронизация «Variable» должна выполняться внутри самого объекта. Невозможно сделать это с помощью реализации List. Поэтому сначала исправьте эту проблему. 2) при использовании итератора самым простым способом является использование синхронизированного блока с использованием списка в качестве объекта блокировки. (Например, 'synchronized (varList) {/ * do stuff * /}'). 3) если вы обязательно используете синхронизированный блок каждый раз, когда вы присоединяетесь к списку, он (и предыдущая часть) не будет проблемой. Хотя если производительность не является проблемой, используйте вместо этого 'CopyOnWriteArrayList', поскольку это полностью ThreadSafe (за исключением самих объектов, конечно!) – n247s

+0

Еще раз спасибо за ответ. Теперь для меня это стало более понятным. Но появился еще один вопрос. Если объекты, которые я хочу изменить, всегда берутся из одного и того же списка, действительно ли мне нужна другая защита для одного экземпляра Variable? Поскольку для получения объекта поток должен передавать синхронизированный (varList) оператор, который невозможен, если в списке работает другой поток. Поэтому я предполагаю, что дополнительный механизм синхронизации нужен только тогда, когда я захочу получить доступ к этим объектам за пределами списка, или я ошибаюсь? –

1

Я хотел бы сделать это гораздо проще. Вам просто нужно сделать все изменения в списке внутри синхронизированного блока, используя тот же объект списка, что и блокировка.

Например, давайте предположим, что у вас есть список

List<Variable> varList; 

уже заполнены с переменными объектов. Теперь, каждый раз, когда вы хотите получить доступ или изменить этот список в вашем коде, вы должны обернуть кусок кода с синхронизированной блока фиксации на список переменных, следующим образом:

synchronized(varList) { 
     // here you access, add, delete, or modify the List 
     // for example: 
     if(varList.get(1) == 3) { 
      varList.remove(1); 
      varList.add(new Variable()); 
     } 
    } 

Для этого, необходимо убедиться в том, что два потоки работают с тем же varList, это varList - это общая переменная для обоих потоков (например, поле или статическая переменная). Вы также можете использовать любой другой новый объект в качестве блокировки, но всегда убедитесь, что он является общим объектом для обоих потоков (а не две переменные с тем же именем, относящиеся к разным объектам в памяти). Но я предпочитаю использовать сам список как блокировку, поэтому он делает код более понятным для меня, я знаю внутри этого блока, я изменяю этот список.