Java RMI和同步方法

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

我认为在远程对象实现上使用synchronized方法 (因此在服务器上运行的实际实现)即使对该方法的调用来自不同的客户端机器(通过代理调用该方法),也会阻止该方法的并发执行……又名Stub)。

我已经看到很多人都有相同的看法,例如在这里看看: Java RMI和线程同步问题

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

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

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

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

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

我认为文章“Java系统的分布式对象模型”( 可在此处获得 )在文本中引用了Wollrath et all, 1996括号内。 然而,我在该论文中找到的唯一相关段落就是这一段:

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

我是以错误的方式解释文本还是事实上说使用RMI时同步方法“不是那么同步”?

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

第二个参考文献无论如何都与第一个参考文献相矛盾。 第二个只是澄清,如果您尝试在存根上进行同步,它将仅在本地锁定,并且不会影响远程服务器的并发性。

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

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

您还应该知道Javamultithreading自1996年以来发生了重大变化。作为原始语言设计一部分的notify()和wait()方法从并发专家和Java 5中得到了很多抨击(2004年,维基说)引入了像ReentrantLock这样的高级并发对象 ,这些对象现在是首选的做事方式。

所以你提到的批评可能是正确的,但已经过时了。

你是对的。 文字错了。 RMI存根是线程安全的,可以由单个客户端JVM中的多个线程同时调用。 我不知道Wollrath的所有声明或文字都说不同,自1997年以来我一直在关注这个话题。

特别:

我认为在远程对象实现上使用synchronized方法(因此在服务器上运行的实际实现)即使对该方法的调用来自不同的客户端机器(通过代理调用该方法),也会阻止该方法的并发执行……又名Stub)。

你是对的。

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

这本书不仅错误,而且说不可能。 RMI究竟如何阻止同步工作?

逻辑上,远程对象中的阻塞很简单。 假设客户端A调用远程对象的同步方法。

然后通过Java的正常操作在服务器上发生阻塞。

要使对远程对象的访问看起来与本地对象完全相同,有必要在实现对象接口的客户端存根中阻止A,并且A有直接访问权限。

垃圾。 远程方法实现synchronized的事实可以完成所有必要的操作。

同样,在将其请求发送到服务器之前,还需要在本地阻止另一台机器上的另一个客户端。

这又是垃圾。

结果是我们需要在不同的机器上同步不同的客户端。

再次垃圾。

另一种方法是仅允许在服务器上进行阻止。

‘允许’? 这是什么意思? synchronized方法是synchronized. 你无法拒绝它。

原则上,这样可以正常工作,但是当客户端在服务器处理其调用时崩溃时会出现问题。

再次垃圾。 没有出现这样的问题。 服务器通过读取超时或写入exception甚至成功完成远程方法从这种情况中恢复。 在所有三种情况下,该方法退出,同步锁定被释放,并且生命继续。

正如我们在Chap中讨论的那样。 8,我们可能需要相对复杂的协议来处理这种情况,这可能会显着影响远程方法调用的整体性能。

废话。

因此,Java RMI的设计者选择仅限制对代理的远程对象的阻塞(Wollrath等,1996)。

我不知道除了你所引用的摘录之外还有什么可以引用的,而且我已多次读过那篇论文。 如果作者想要依赖这篇论文,他们应该为章和节提供引文和适当的引用。

无论如何,RMI的设计者没有做出这样的选择。 没有这样的选择。 无论RMI设计者可能想要什么,也可能不希望synchronized都是synchronized的,类似的notify()wait()也是final. 他们不能自由做出任何选择。 您提供的引用不是“选择”:它只是关于Java语义的声明。

我是以错误的方式解释文本还是事实上说使用RMI时同步方法“不是那么同步”?

我认为你正确地阅读它,它完全是完全错误的,不仅错误而且显然是错误的。 怎么可能是对的? Java RMI不会, 实际上也不能以任何方式更改,删除或扩展synchronized的语义。