Другие ответы предложили использовать Collections.synchronizedList()
.
List<User> list = Collections.synchronizedList(new ArrayList<>());
Это мощь работы, но вы должны быть очень осторожны. Такой список только поточно-безопасен для вызовов отдельных методов непосредственно в этом списке. Однако многие операции включают в себя несколько операций в списке, таких как итерация по списку. Например, можно написать следующий код для отправки сообщения для каждого пользователя в чате:
for (User u : list)
u.sendMessage(msg);
Это неудачу с ConcurrentModificationException
если пользователь добавляется или удаляется из списка во время итерации. Это происходит потому, что эта форма цикла for получает Iterator
из списка и делает повторный доступ к списку через вызовы hasNext
и next
. Список может быть изменен между этими обращениями. Если список изменен, Iterator
обнаруживает это и выдает исключение.
В Java SE 8, можно было бы вместо того, чтобы написать следующее:
list.forEach(u -> u.sendMesage(msg));
Это безопасно, потому что замок в Листом проводится в течение всего срока действия forEach
вызова.
(Еще одно предложение заключается в использовании Set
вместо List
. Это может быть хорошей идеей, и по другим причинам, но он страдает от тех же проблем параллелизма в List
.)
Вероятно, лучший способ справиться с это создать свой собственный объект, который содержит список (или набор) пользователей и любых связанных данных и определяет определенный набор операций в вашем контейнере пользователей и связанных с ним данных. Затем добавьте правильную синхронизацию вокруг методов вашего объекта контейнера. Это позволяет потокобезопасные обновления другим данным, кроме только вашего списка или набора пользователей.
И наконец, volatile
не требуется, если поле списка инициализируется во время строительства, прежде чем объект станет видимым для других потоков. В условиях RMI, если строительство завершено до экспорта объектов, вы в безопасности. Хорошей практикой является создание поля final
, так что после инициализации инициализированное значение, как известно, будет видимым для всех других потоков и не может измениться после этого.
Да, он должен быть синхронизирован, используйте [синхронизированный список] (http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList%28java.util.List% 29) –