Возможно ли синхронизировать элементы данных, а не методы?

#java

#java

Вопрос:

Прочитав здесь разделы о методах синхронизации Java, я попытался реализовать это в своей многопользовательской игре, потому что многие потоки открыты и пытаются получить доступ к одному и тому же ресурсу. Я синхронизировал методы, но это мне не помогает, поскольку, если у меня есть элемент данных с именем ArrayList ClientConnection; и доступные методы:

 int getArrayListSize() {
    clientConnection.size();
}

void addConnection(ServerConnection i_connection) {
    clientConnection.add(i_connection);
}

void removeConnection(ServerConnection i_connection) {
    int index = clientConnections.indexOf(i_Connection);
    clientConnections.remove(index);
}

ServerConnection getClientFromArrayListByIndex(int i_Index) {
    ServerConnection client = this.clientConnections.get(i_Index);
}
  

Я пытался создать глобальный синхронизированный метод, чтобы всякий раз, когда кто-то хочет использовать один из методов, ему нужно передать тип операции и другие данные, и он блокирует функцию.
Проблема в том, что есть 2 функции, которые возвращают void, 1 возвращает int, а 1 возвращает ServerConnection, поэтому я не могу создать этот глобальный метод.
Мой вопрос, можно ли заблокировать элементы данных, а не методы в Java, чтобы я мог заблокировать элемент данных ClientConnection?
Спасибо.

Комментарии:

1. Мне кажется, что getArrayListSize и getClientFromArrayListByIndex не принадлежат общедоступному интерфейсу вашего объекта. Если бы вы могли показать нам, каким будет фактическое использование, мы могли бы направить вас в правильном направлении.

Ответ №1:

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

 synchronized int getArrayListSize() { ... }
synchronized void addConnection(ServerConnection i_connection) { ... }
etc.
  

Комментарии:

1. проблема в том, что мне нужно больше методов, которые могут получить доступ к списку, например, удаление соединения или итерация для поиска соединения…

2. также синхронизируйте эти методы.

Ответ №2:

Вы можете синхронизировать функцию, используя synchronized ключевое слово. Например:

 synchronized ServerConnection getClientFromArrayListByIndex(int i_Index) { 
    ServerConnection client = this.clientConnections.get(i_Index);
    // ...
}
  

Комментарии:

1. Проблема, с которой я сталкиваюсь, заключается в том, что один поток может удалить соединение с сервером из ClientConnection ArrayList, и в то же время другой поток пытается получить размер arraylist или получить объект serverconnection в этом конкретном индексе, который был удален, и это не нормально

2. Затем вы должны вручную указать объект блокировки. Вы можете синхронизировать на основе clientConnection ArrayList, и только один поток сможет получить доступ к этому объекту одновременно. Общее эмпирическое правило заключается в синхронизации с общим ресурсом (и, следовательно, причиной потенциальной проблемы). Вы также можете изучить возможность использования структур данных conncurrent.

3. Этот сценарий может вызвать исключение ArrayIndexOutOfBoundsException, но он не оставит список в неопределенном состоянии из-за проблемы параллелизма. Просто правильно задокументируйте метод и / или сделайте так, чтобы он возвращал значение null, если индекс больше не действителен, и / или создайте пользовательское исключение, а не исключение ArrayIndexOutOfBoundsException по умолчанию, если индекс больше не действителен.

4. вы говорите, что я могу заблокировать список массивов?

5. Да, просто заключая обращения к ArrayList в synchronized(clientConnections) { ... } блоки. Но, как упоминали другие в этом ответе и в других, вам также необходимо учитывать производительность. В идеале, что бы вы хотели, чтобы произошло, если есть конфликт для этого общего ресурса? Например, вы могли бы использовать BlockingQueue , который, по сути, просто ставит операции в очередь. Если сначала выполняется удаление, а затем чтение, операция чтения должна будет дождаться завершения удаления. Но тогда какой-то клиент может просто застрять в ожидании. В игре это действительно плохо.

Ответ №3:

Если у вас есть один класс, вы можете просто добавить префикс к своим методам synchronized ; тогда в любом из этих методов есть только один поток. Но, пожалуйста, подумайте, действительно ли вам это нужно; это может замедлить выполнение. Если у вас есть общедоступные поля, вы должны сделать их закрытыми и создать методы получения.

Комментарии:

1. Это было важное замечание; вы не должны синхронизировать все методы всех классов. Вы должны подумать об этом. Но, на мой взгляд, лучше правильно, чем быстро.

2. синхронизация всех методов приведет к замедлению работы приложения, верно? что мне делать, если у меня разные типы операций с одним и тем же списком массивов, например: add, remove, getsize и т. Д. … Если у меня есть 3 потока, и каждый из них использует разные методы одновременно, это может вызвать большую проблему.

3. Вы можете использовать synchronizedList или синхронизировать по arraylist.

Ответ №4:

Вы могли бы использовать Collections.synchronizedList() , чтобы обернуть свой List в тот, для которого синхронизированы все методы.

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

Комментарии:

1. Это звучит как отличное решение, у вас есть пример использования?

2. За исключением метода removeConnection(), который может завершиться неудачей — он выполняет две операции, которые синхронизируются для каждого вызова, но не являются атомарной единицей (и должны быть). Есть вероятность, что другой поток может изменить список между вызовами indexOf() и remove() .

3. Я не заметил, что происходит в removeConnection() — учитывая код там, это создало бы условие гонки и, следовательно, было бы неуместным. Однако я упоминал, что уместно ли это, зависит от того, что еще происходит в методах…

4. @Todd removeConnection(...) можно было бы тривиально переписать для использования List#remove(Object o) , и в этом случае это не было бы проблемой.

5. Не говоря уже о том, что getClientFromArrayListByIndex(...) вызов создает некоторые серьезные проблемы с блокировкой для любой реализации на основе списков.