Java RMI 和同步方法

2022-09-03 07:09:52

我正在研究《分布式系统》(由Tanenbaum和Van Steen撰写)一书,他们说了一些似乎与Java RMI和同步方法上许多人的想法相冲突的东西。

我的想法是,在远程对象实现上使用同步方法(因此在服务器上运行的实际实现)并发执行该方法是被禁止的,即使对该方法的调用来自不同的客户端计算机(通过代理调用该方法...又名存根)。

我看到很多人都有同样的看法,看看这里的例子:Java RMI和线程同步问题

在书中,它说在使用RMI时不会阻止同步方法的并发执行。

以下是本书的相关摘录(您只能阅读粗体句子,但如果您愿意,可以阅读上下文):

从逻辑上讲,远程对象中的阻止很简单。假设客户端 A 调用远程对象的同步方法。若要使对远程对象的访问看起来始终与对本地对象的访问完全相同,有必要在实现对象接口且 A 具有直接访问权限的客户端存根中阻止 A。同样,在将其请求发送到服务器之前,还需要在本地阻止另一台计算机上的另一个客户端。结果是我们需要在不同的机器上同步不同的客户端。正如我们在第 6 章中讨论的那样,分布式同步可能相当复杂。

另一种方法是仅允许在服务器上进行阻止。原则上,这可以正常工作,但是当客户端在服务器处理其调用时崩溃时,就会出现问题。正如我们在第 8 章中所讨论的,我们可能需要相对复杂的协议来处理这种情况,这可能会显著影响远程方法调用的整体性能。

因此,Java RMI的设计者选择将远程对象上的阻塞限制为仅代理(Wollrath等人,1996)。这意味着将阻止同一进程中的线程并发访问同一远程对象,但不同进程中的线程不会。显然,这些同步语义是棘手的:在语法级别(即,在阅读源代码时),我们可能会看到一个漂亮,干净的设计。仅当实际执行分布式应用程序时,才可能观察到应该在设计时处理的意外行为。[...]

我认为论文“Java系统的分布式对象模型”(可在此处获得)在文本中通过括号之间的注释引用。然而,我在那篇论文中发现的唯一相关段落是这个:Wollrath et all, 1996

由于本地和远程对象的故障模式不同,分布式等待和通知需要在所涉及的实体之间使用更复杂的协议(例如,客户端崩溃不会导致远程对象永远锁定),因此,不能轻松地将其放入Java中的本地线程模型中。因此,客户端可以对远程引用使用 notify 和 wait 方法,但该客户端必须知道此类操作不会涉及实际的远程对象,而只涉及远程对象的本地代理(存根)。

我是否以错误的方式解释文本,或者实际上声明同步方法在使用RMI时“不那么同步”?


答案 1

您的第一个参考是,在单个 VM 实例中,RMI 存根(RMI 服务器的客户端)上的调用将在内部同步。也就是说,存根(或代理,如文本似乎称之为)本身将阻止多个线程同时调用远程服务器上的方法。但是,它阐明了两个虚拟机,每个虚拟机都具有远程服务器的存根,不会被阻止并发调用远程服务器(这是显而易见的,因为它们不能共享锁定,并且 RMI 本身不会阻止服务器的并发)。如果这是不希望的,RMI 服务器必须实现锁定机制以防止多个并发调用。

第二个引用与第一个引用并不矛盾。第二个只是澄清,如果您尝试在存根上进行同步,它只会在本地锁定,并且不会影响远程服务器的并发性。

结合这两个文本,我们可以读到,在存根上进行同步将阻止同一 VM 中的多个线程同时访问远程数据库,但不会阻止不同 VM 中的线程进行并发访问。


答案 2

据我所知,每次调用RMI服务器都会在服务器端创建一个新线程(由我2000年的日志文件见证)。如果您在服务器端进行同步,则应该是安全的。正如你发布的那样,我遇到了一些来自文学的古老警告。作为一名实践者,我更喜欢运行该软件一个月左右,并认为它足够稳定,可以投入生产。我很抱歉,如果这不是不令人满意的。


推荐