Java RMI и синхронизированные методы
Я изучаю книгу "Распределенные системы" (Tanenbaum & Van Steen), и они говорят что-то, что кажется противоречащим тому, что многие вместо этого думают о Java RMI и синхронизированных методах.
Я подумал, что при использовании синхронизированного метода в реализации удаленного объекта (т.е. реальной реализации, выполняющейся на сервере) одновременное выполнение этого метода предотвращается, даже если вызовы этого метода происходят с разных клиентских компьютеров (вызов метода через прокси-сервер).... он же заглушка).
Я видел, что многие люди придерживаются одинакового мнения, посмотрите здесь, например: вопросы Java RMI и Thread Synchronization.
В книге вместо этого говорится, что одновременное выполнение синхронизированных методов не предотвращается при использовании RMI.
Вот соответствующий отрывок из книги (вы можете прочитать только жирное предложение, но вы можете прочитать контекст, если хотите):
Логически, блокировка в удаленном объекте проста. Предположим, что клиент A вызывает синхронизированный метод удаленного объекта. Чтобы доступ к удаленным объектам всегда выглядел точно так же, как к локальным объектам, необходимо было бы заблокировать A в заглушке на стороне клиента, которая реализует интерфейс объекта и к которому A имеет прямой доступ. Аналогично, другой клиент на другом компьютере должен быть также заблокирован локально, прежде чем его запрос может быть отправлен на сервер. Как следствие, нам нужно синхронизировать разных клиентов на разных машинах. Как мы обсуждали в гл. 6, распределенная синхронизация может быть довольно сложной.
Альтернативный подход - разрешить блокировку только на сервере. В принципе, это работает нормально, но проблемы возникают, когда происходит сбой клиента, когда его вызов обрабатывается сервером. Как мы обсуждали в гл. 8, нам может потребоваться относительно сложные протоколы для обработки этой ситуации, которые могут существенно повлиять на общую производительность удаленных вызовов методов.
Поэтому разработчики Java RMI решили ограничить блокировку удаленных объектов только прокси (Wollrath et al., 1996). Это означает, что потокам в одном и том же процессе будет запрещено одновременный доступ к одному и тому же удаленному объекту, а потокам в разных процессах - нет. Очевидно, что эта семантика синхронизации сложна: на синтаксическом уровне (т. Е. При чтении исходного кода) мы можем увидеть хороший, чистый дизайн. Только когда фактически выполняется распределенное приложение, можно наблюдать непредвиденное поведение, которое должно было быть решено во время разработки. [...]
Я думаю, что статья "Распределенная объектная модель для системы Java" ( доступна здесь) упоминается в тексте в примечании Wollrath et all, 1996
между скобками. Однако единственный релевантный абзац, который я нашел на этом документе, это:
Из-за различных режимов отказа локальных и удаленных объектов распределенное ожидание и уведомление требуют более сложного протокола между участвующими объектами (так, например, сбой клиента не приводит к тому, что удаленный объект блокируется навсегда), и как таковой, не может быть легко встроен в локальную модель потоков в Java. Следовательно, клиент может использовать методы уведомления и ожидания для удаленной ссылки, но этот клиент должен знать, что такие действия не будут затрагивать фактический удаленный объект, только локальный прокси (заглушка) для удаленного объекта.
Я неправильно интерпретирую текст, или на самом деле утверждается, что синхронизированные методы "не так синхронизированы" при использовании RMI?
4 ответа
Ваша первая ссылка говорит о том, что в одном экземпляре виртуальной машины вызовы на заглушке RMI (клиент RMI-сервер) будут внутренне синхронизированы. Таким образом, заглушка (или прокси-сервер, как кажется, в тексте называется) сама по себе не позволит нескольким потокам одновременно вызывать метод на удаленном сервере. Однако поясняется, что две виртуальные машины, каждая с заглушками для удаленного сервера, не будут заблокированы от одновременного вызова удаленного сервера (что очевидно, поскольку они не могут совместно использовать блокировку, а сам RMI не предотвращает параллелизм на сервере). Если это нежелательно, сервер RMI должен будет реализовать механизм блокировки, чтобы предотвратить множественные одновременные вызовы.
Второе упоминание ни в коем случае не противоречит первому. Второй просто поясняет, что если вы попытаетесь выполнить синхронизацию на заглушке, она будет заблокирована только локально и не повлияет на параллелизм удаленного сервера.
Комбинируя два текста, мы можем прочитать, что синхронизация на заглушке предотвратит одновременный доступ нескольких удаленных потоков в одной виртуальной машине к удаленному, но не предотвратит одновременный доступ потоков в разных виртуальных машинах.
Ты прав. Текст неверный. Заглушки RMI поточно-ориентированы и могут быть вызваны одновременно несколькими потоками в одной клиентской JVM. Я не знаю ни одного заявления или текста Уоллрата и всего, что говорит о чем-то ином, и я слежу за этой темой с 1997 года.
В частности:
Я подумал, что при использовании синхронизированного метода в реализации удаленного объекта (т.е. реальной реализации, выполняющейся на сервере) одновременное выполнение этого метода предотвращается, даже если вызовы этого метода происходят с разных клиентских компьютеров (вызов метода через прокси-сервер).... он же заглушка).
Ты прав.
В книге вместо этого говорится, что одновременное выполнение синхронизированных методов не предотвращается при использовании RMI.
Книга не только ошибочна, но и заявляет о невозможности. Как именно RMI может препятствовать работе синхронизации?
Логически, блокировка в удаленном объекте проста. Предположим, что клиент A вызывает синхронизированный метод удаленного объекта.
Затем происходит блокировка на сервере при нормальной работе Java.
Чтобы доступ к удаленным объектам всегда выглядел точно так же, как к локальным объектам, необходимо было бы заблокировать A в заглушке на стороне клиента, которая реализует интерфейс объекта и к которому A имеет прямой доступ.
Мусор. Тот факт, что реализация удаленного метода synchronized
делает все что нужно.
Аналогично, другой клиент на другом компьютере должен быть также заблокирован локально, прежде чем его запрос может быть отправлен на сервер.
Опять это мусор.
Как следствие, нам нужно синхронизировать разных клиентов на разных машинах.
Снова мусор.
Альтернативный подход - разрешить блокировку только на сервере.
'Разрешать'? Что это значит? synchronized
метод synchronized.
Вы не можете это запретить.
В принципе, это работает нормально, но проблемы возникают, когда происходит сбой клиента, когда его вызов обрабатывается сервером.
Снова мусор. Таких проблем не возникает. Сервер восстанавливается из этой ситуации либо через тайм-аут чтения, либо через исключение при записи, либо даже при успешном завершении удаленного метода. Во всех трех случаях метод завершается, блокировка синхронизации снимается, и срок службы продолжается.
Как мы обсуждали в гл. 8, нам может потребоваться относительно сложные протоколы для обработки этой ситуации, которые могут существенно повлиять на общую производительность удаленных вызовов методов.
Ерунда.
Поэтому разработчики Java RMI решили ограничить блокировку удаленных объектов только прокси (Wollrath et al., 1996).
Я не знаю, к чему еще это может относиться, кроме цитаты, которую вы цитировали, и я читал эту статью много раз. Если авторы хотят полагаться на этот документ, они должны были предоставить цитату и надлежащую цитату к главе и стиху.
В любом случае дизайнеры RMI не сделали такого выбора. Не было такого выбора, чтобы сделать. synchronized
является synchronized
все, что дизайнеры RMI могли или не могли желать, и аналогично notify()
а также wait()
являются final.
Они не могли делать выбор. Предоставленная вами цитата не является "выбором": это всего лишь утверждение о семантике Java.
Я неправильно интерпретирую текст, или на самом деле утверждается, что синхронизированные методы "не так синхронизированы" при использовании RMI?
Я думаю, что вы читаете это правильно, и это совершенно и совершенно неправильно, и не только неправильно, но, очевидно, неправильно. Как это может быть правильно?
Java RMI не изменяет, не удаляет и не расширяет семантику synchronized
в любом случае.
Насколько я знаю, каждый вызов RMI-сервера будет создавать новый поток (засвидетельствованный моими лог-файлами с 2000 года) на стороне сервера. Если вы делаете синхронизацию на стороне сервера, вы должны быть в безопасности. Когда вы писали, я столкнулся с некоторыми древними предупреждениями из литературы. Как практикующий специалист, я предпочел запускать программное обеспечение в течение месяца или около того и решил, что оно достаточно стабильно для работы. Я извиняюсь, если это не удовлетворяет.
Вы также должны знать, что многопоточность Java значительно изменилась с 1996 года. Методы notify() и wait(), которые были частью оригинального языкового дизайна, получили много шума от экспертов по параллелизму и в Java 5 (2004).) были введены высокоуровневые объекты параллелизма, такие как ReentrantLock, которые теперь являются предпочтительным способом работы.
Таким образом, упомянутая вами критика, вероятно, правильна, но устарела.