2014-01-29 3 views
0

В настоящее время я пишу простой чат-приложение на Java. Он имеет серверное приложение и клиентское приложение, написанное на Java и использующее RMI для связи.java - RMI и синхронизация

На сервере добавлен ArrayList активных пользователей, который обновляется при включении и отключении пользователей. Каждое клиентское соединение с сервером имеет собственный поток.

Когда происходит новое соединение (то есть новый клиент соединяется), создается новый поток, и в этом потоке обновляется ArrayList, чтобы отразить нового пользователя. Аналогично, когда соединение потеряно, обновляется ArrayList, чтобы отразить отключение.

Очевидно, что с большим количеством клиентов будет много одновременного доступа к ArrayList. Поэтому мой вопрос заключается в том, должен ли быть синхронизирован любой из методов доступа/обновления этого ArrayList?

+0

Да, он должен быть синхронизирован, используйте [синхронизированный список] (http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList%28java.util.List% 29) –

ответ

1

Другие ответы предложили использовать 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, так что после инициализации инициализированное значение, как известно, будет видимым для всех других потоков и не может измениться после этого.

0

Вы должны использовать Collections.synchronizedList(), если хотите работать со списками в нескольких потоках.

Это предложение было дано here.

1

Использование Collections.synchronizedList(new ArrayList<User>());

Во-вторых вы также можете сделать

volatile List<User> list = Collections.synchronizedList(new ArrayList<User>());

для гарантированной видимости между потоками.

Примечание: Я бы также предложил использовать Set вместо List, чтобы избежать дублирования в случае возникновения каких-либо ошибок или угловых случаев. См. Следующую строку кода для справки.

volatile Set<User> onlineUsers = Collections.synchronizedSet(new HashSet<User>()); 
+0

'volatile' гарантирует только видимость между потоками, а не синхронизацию. –

+1

Уже поставлен Collections.synchronizedSet для синхронизации. –

+0

Не видели синхронизированный список/набор раньше, посмотрим в него - спасибо. Также обеспечит добавление ключевого слова volatile. – swiss196