List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
Действительно ли нужен блок "synchronized (list) {}"?
List<String> list = Collections.synchronizedList(new ArrayList<String>());
synchronized (list) {
list.add("message");
}
Действительно ли нужен блок "synchronized (list) {}"?
Вам не нужно синхронизировать, как вы помещаете в свой пример. ОДНАКО, очень важно, вам нужно синхронизировать список, когда вы его повторяете (как указано в Javadoc):
Обязательно, чтобы пользователь вручную синхронизировал список при его повторении:
List list = Collections.synchronizedList(new ArrayList()); ... synchronized(list) { Iterator i = list.iterator(); // Must be in synchronized block while (i.hasNext()) foo(i.next()); }
Это зависит от точного содержимого блока synchronized
:
Если блок выполняет одиночную, атомную операцию в списке (как в вашем примере), synchronized
является излишним.
Если блок выполняет несколько операций в списке - и должен поддерживать блокировку на протяжении сложной операции - тогда synchronized
не лишний. Один из распространенных примеров этого - итерирование по списку.
Исходный код для метода Collections.synchronizedList add:
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);}
}
Итак, в вашем примере не требуется добавлять синхронизацию.
Также важно отметить, что любые методы, которые используют Итераторы, например Collections.sort(), также должны быть инкапсулированы внутри синхронизированного блока.
Прочтите этот Oracle Doc
В нем говорится: "Обязательно, чтобы пользователь вручную синхронизировал в возвращенном списке при итерации по нему"
Как и то, что было упомянуто другими, синхронизированные коллекции поточно-безопасные, но составные действия для этих коллекций по умолчанию не гарантируют потоки.
Согласно JCIP, общие действия соединения могут быть
Блок синхронизированного кода OP не является составным действием, поэтому нет никакой разницы, добавьте его или нет.
Возьмем пример из JCIP и немного изменим его, чтобы выяснить, почему необходимо защищать составные действия с помощью блокировки.
Существует два метода, которые работают в одной коллекции list
, завернутой в Collections.synchronizedList
public Object getLast(List<String> list){
int lastIndex = list.size() - 1;
return list.get(lastIndex);
}
public void deleteLast(List<String> list){
int lastIndex = list.size() - 1;
list.remove(lastIndex);
}
Если методы getLast
и deleteLast
вызываются одновременно двумя разными потоками, ниже могут произойти чередования, а getLast
будет бросать ArrayIndexOutOfBoundsException
. Предположим, что ток lastIndex
равен 10.
Тема A (deleteLast) → удалить
Тема B (getLast) -------------------- > get
Поток A remove
элемент перед операцией get
в потоке B. Таким образом, поток B по-прежнему использует 10 как метод lastIndex
для вызова метода list.get
, это приведет к одновременной проблеме.