Java RMI и вопросы синхронизации потоков

На самом деле у меня есть два вопроса о Java RMI и синхронизации потоков:

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

2) У меня есть метод, который сервер выполняет периодически. Он используется для очистки. Я должен убедиться, что этот конкретный метод не выполняется, когда какой-либо метод RMI запускается / используется удаленными клиентами. Кроме того, когда этот метод работает, вызовы RMI не должны быть возможными. Т.е. клиенты должны ждать. Есть идеи, как я могу это сделать? Я читал о замках, но я не знаю, как использовать их в этом сценарии.

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

Я также написал метод очистки внутри интерфейса RMI как синхронизированный. Когда я запускал его для тестирования, между методами не было конфликтов, но я не уверен.

Спасибо за ваше время и ответы.

4 ответа

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

RMI не предоставляет такой гарантии самостоятельно (в отличие от EJB), и два вызова одного и того же удаленного объекта могут выполняться одновременно, если вы не реализуете некоторую синхронизацию. Ваш подход верен, и синхронизация всех методов действительно гарантирует, что никакие два из них не будут работать одновременно на одном и том же объекте. Примечание: ключевое слово synchronized одно эквивалентно synchronized( this ),

2) У меня есть метод, который сервер выполняет периодически. Он используется для очистки. Я должен убедиться, что этот конкретный метод не выполняется, когда какой-либо метод RMI запускается / используется удаленными клиентами.

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

protected Object lock = new Object();

По соглашению люди используют Object для этого. Затем вам нужно захватить блокировку в вашей периодической работе с synchronized( remoteObj.lock ) { ... }при условии, что это в той же упаковке.

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

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

Я также написал метод очистки внутри интерфейса RMI как синхронизированный. Когда я запускал его для тестирования, между методами не было конфликтов, но я не уверен.

Если я хорошо понимаю, вы хотели бы, чтобы логика очистки была статическим методом? Статический метод с synchronized один захватывает класс. "Обычный" метод с synchronized захватывает блокировку на экземпляре объекта. Это не те же самые скрытые блокировки!

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

Скелет:

public class MyRemoteClass {
   public static Object lock = new Object();

   public void doStuff()
   {
       synchronized( lock ) { ... }
   }
}

public class Cleanup {
   public static void doIt()
   {
       synchronized( MyRemoteClass.lock ) { ... }
   }
}
  1. Для каждого вызова от клиента RMI сервер RMI будет выполнять вызов в новом потоке. Вам нужно только синхронизировать доступ к общим объектам.

  2. Другой поток или таймер не остановит ваш сервер от приема звонков со стороны клиента. Для этого требуется синхронизация, наилучшая практика зависит от того, как долго выполняется задание очистки, может ли оно быть прервано, или будет ли возможность поместить запросы в очередь и т. Д. Самый простой способ - позволить методам RMI ожидать блокировки, как уже было выполнено. описанный ewernli.

РЕДАКТИРОВАТЬ: Согласно вашему комментарию, скелет, который демонстрирует, как достичь этого вида базовой синхронизации. Поскольку теперь все является взаимоисключающим, вы не можете ожидать высокой производительности при участии нескольких клиентов. В любом случае это будет соответствовать вашим требованиям. (Я надеюсь). Если ваш проект растет, вы должны прочитать Учебник по параллелизму

Object mutex = new Object();

int rmiMethod1() {
    synchronized (mutex) {
        doWhatNeeded1();
    }
}

int rmiMethod2() {
    synchronized (mutex) {
        doWhatNeeded2();
    }
}

// in your cleanup thread
void run() {
    synchronized (mutex) {
        cleanUp();
    }
}

Чтобы прояснить всю эту путаницу:

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

  2. Если вы синхронизируете задачу очистки на удаленном объекте, он присоединится (1).

  3. Вы не можете определить удаленные методы как статические.

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

Java RMI и синхронизированные методы

Таким образом, вам нужно реализовать какую-то не-RMI, чисто серверную синхронизацию самостоятельно. Затем вы можете вызывать эти чистые блокировки на стороне сервера из ваших удаленных методов; но вам нужен дополнительный уровень косвенности.

Чтобы проверить ваш код, проще всего приостановить ваши потоки под хорошим отладчиком, таким как Eclipse. Eclipse четко покажет вам, какая приостановленная нить удерживает, какая блокировка блокирует какую-либо другую нить.

Другие вопросы по тегам