我正在研究《分布式系统》(Tanenbaum 和 Van Steen 所著)一书,他们所说的内容似乎与许多人对 Java RMI 和同步方法的看法相冲突。
我的想法是,即使对该方法的调用来自不同的客户端计算机,在远程对象实现(因此在服务器上运行的实际实现)上使用同步方法也会阻止该方法的并发执行(通过代理调用该方法...又名存根)。
我发现很多人都有相同的观点,例如:Java RMI 和线程同步问题
书中相反说,使用 RMI 时不会阻止同步方法的并发执行。
以下是书中的相关摘录(您只能阅读粗体句子,但如果您愿意,可以阅读上下文):
从逻辑上讲,阻塞在远程对象中
很简单。假设客户端A调用
远程的同步方法
目的。访问远程
物体看起来总是一模一样
对于本地对象,这将是
需要阻止A
实现的客户端存根
对象的接口以及 A 所拥有的接口
直接访问。同样,另一个
不同机器上的客户端会
本地也需要屏蔽
在其请求可以发送到
服务器。结果就是我们
需要同步不同的客户端
在不同的机器上。正如我们所讨论的
在第 1 章中。 6、分布式
同步可能相当复杂。
另一种方法是
仅允许在服务器上进行阻止。在
原则上,这很好用,但是
当客户端崩溃时就会出现问题
当它的调用正在处理时
由服务器。正如我们在
第一章8、我们可能需要相对
复杂的协议来处理这个问题
情况,并且这可能
显着影响整体
远程方法的性能
调用。
因此,Java RMI的设计者
已选择限制阻止
远程对象仅针对代理
(Wollrath 等人,1996)。这意味着
同一进程中的线程将
可以防止同时发生
访问同一个远程对象,但是
不同进程中的线程将
显然,这些同步
语义很棘手:在语法上
级别(即阅读源代码时)
我们可能会看到一个漂亮、干净的设计。仅有的
当分布式应用程序是
实际执行,未预料到的
可以观察到的行为应该
已经在设计时处理过了。
[...]
我认为论文“Java系统的分布式对象模型”(可用此处)在文本中通过括号之间的注释 Wollrath et all, 1996
引用。然而,我在那篇论文中找到的唯一相关段落是这个:
由于本地和远程的故障模式不同
对象、分布式等待和通知需要
所涉及实体之间的更复杂的协议
(例如,客户端崩溃不会
导致远程对象被永远锁定),并且
这样,不能轻易地安装到本地线程中
Java 中的模型。因此,客户端可以使用通知并等待
远程引用上的方法,但该客户端必须是
意识到此类操作不会涉及实际的远程
对象,仅为远程的本地代理(存根)
对象。
我是否以错误的方式解释了文本,或者实际上声明在使用 RMI 时同步方法“不那么同步”?
I'm studying the book "Distributed Systems" (by Tanenbaum & Van Steen) and they say something that seems to conflict to what seems to be instead thought by many on Java RMI and synchronized methods.
What I thought is that using a synchronized method on a Remote Object implementation (so the real implementation running at the server) concurrent execution of that method is prevented even when the calls to that method are from different clients machines (calling the method via a Proxy... aka a Stub).
I've seen that a lot of people have the same opinion, look here for example: Java RMI and Thread Synchronization questions
In the book it's instead said that concurrent execution of synchronized methods is not prevented when using RMI.
Here's the relevant excerpt from the book (you can read the bold sentence only, but you can read the context if you prefer to):
Logically, blocking in a remote object
is simple. Suppose that client A calls
a synchronized method of a remote
object. To make access to remote
objects look always exactly the same
as to local objects, it would be
necessary to block A in the
client-side stub that implements the
object's interface and to which A has
direct access. Likewise, another
client on a different machine would
need to be blocked locally as well
before its request can be sent to the
server. The consequence is that we
need to synchronize different clients
at different machines. As we discussed
in Chap. 6, distributed
synchronization can be fairly complex.
An alternative approach would be to
allow blocking only at the server. In
principle, this works fine, but
problems arise when a client crashes
while its invocation is being handled
by the server. As we discussed in
Chap. 8, we may require relatively
sophisticated protocols to handle this
situation, and which that may
significantly affect the overall
performance of remote method
invocations.
Therefore, the designers of Java RMI
have chosen to restrict blocking on
remote objects only to the proxies
(Wollrath et al., 1996). This means
that threads in the same process will
be prevented from concurrently
accessing the same remote object, but
threads in different processes will
not. Obviously, these synchronization
semantics are tricky: at the syntactic
level (ie, when reading source code)
we may see a nice, clean design. Only
when the distributed application is
actually executed, unanticipated
behavior may be observed that should
have been dealt with at design time.
[...]
I think that the paper "A Distributed Object Model for the Java System" (available here) is referenced in the text by the note Wollrath et all, 1996
between parenthesis. However the only relevant paragraph I've found on that paper is this one:
Due to the differing failure modes of local and remote
objects, distributed wait and notification requires a
more sophisticated protocol between the entities involved
(so that, for example, a client crash does not
cause a remote object to be locked forever), and as
such, cannot be easily fitted into the local threading
model in Java. Hence, a client can use notify and wait
methods on a remote reference, but that client must be
aware that such actions will not involve the actual remote
object, only the local proxy (stub) for the remote
object.
Am I interpreting the text in the wrong way or is in fact stated that synchronized methods are "not so synchronized" when using RMI?
发布评论
评论(4)
您的第一个参考文献所说的是,在单个 VM 实例中,RMI 存根(RMI 服务器的客户端)上的调用将在内部同步。也就是说,存根(或代理,正如文本中所说的那样)本身将阻止多个线程同时调用远程服务器上的方法。然而,它澄清了,两个分别具有远程服务器存根的虚拟机不会被阻止并发调用远程服务器(这是显而易见的,因为它们不能共享锁,并且 RMI 本身不会阻止服务器上的并发)。如果这是不希望的,RMI 服务器将必须实现锁定机制以防止多个并发调用。
第二个参考文献无论如何与第一个参考文献并不矛盾。第二个只是澄清,如果您尝试在存根上进行同步,它只会在本地锁定,并且不会影响远程服务器的并发性。
结合这两篇文章,我们可以看出,在存根上进行同步将阻止同一虚拟机中的多个线程并发访问远程服务器,但不会阻止不同虚拟机中的线程并发访问。
What your first reference is saying is that within a single VM instance, invocations on an RMI Stub (client to an RMI server) will be internally synchronized. That is, the stub (or proxy, as the text seems to call it) itself will prevent multiple threads from concurrently invoking a method on the remote server. It clarifies, however, that two VMs each with stubs for a remote server will not be blocked from concurrently invoking the remote server (which is obvious, because they cannot share a lock, and RMI itself does not prevent concurrency at the server). If this is undesirable, the RMI server will have to implement a locking mechanism to prevent multiple concurrent invocations.
The second reference does not in anyway contradict the first. The second merely clarifies that if you try to synchronize on a stub, it will only be locked locally, and will not impact the concurrency of the remote server.
Combining the two texts, we can read that synchronizing on a stub will prevent the multiple threads in the same VM from concurrently accessing the remote, but will not prevent threads in distinct VMs from concurrent access.
据我所知,对 RMI 服务器的每次调用都会在服务器端创建一个新线程(我的 2000 年日志文件证明了这一点)。如果您在服务器端进行同步,那么您应该是安全的。正如您所发表的,我面临着一些来自文献的古老警告。作为一名实践者,我更喜欢运行该软件一个月左右,并认为它足够稳定,可以用于生产。如果这还不能令人满意,我很抱歉。
As far as I know each call to an RMI server will create a new thread (witnessed by my log files from 2000) on server side. If you do synchronizing at server side you should be safe. I faced some ancient warnings from literature as you posted. As an practicioner I prefered to run the software for a month or so and decided it was stable enough for for production. I'm Sorry if this isn't not satisfying.
您还应该意识到,自 1996 年以来,Java 多线程已经发生了显着变化。作为原始语言设计一部分的 notify() 和 wait() 方法受到了并发专家和 Java 5(2004 年,wiki 说)的强烈批评。 ) 高级并发对象,例如 ReentrantLock 被引入,这是现在的首选方式做事的。
所以你提到的批评可能是正确的,但已经过时了。
You should also be aware that Java multi-threading has changed significantly since 1996. The notify() and wait() methods that were part of the original language design got a lot of flack from concurrency experts and in Java 5 (2004, says wiki) high level concurrency objects like the ReentrantLock were introduced which are now the preferred way of doing things.
So the criticisms you mention are probably correct, but outdated.
你是对的。文字错误。 RMI 存根是线程安全的,可以由单个客户端 JVM 中的多个线程同时调用。我不知道 Wollrath 等人有任何不同的声明或文字,而且我自 1997 年以来一直在关注这个主题。
具体来说:
你是对的。
这本书不仅是错误的,而且还指出了不可能。 RMI 究竟如何阻止同步工作?
然后,通过 Java 的正常操作,在服务器上发生阻塞。
垃圾。事实上,远程方法实现是
同步的
,它完成了所有必要的工作。这又是垃圾。
又是垃圾。
'允许'?这意味着什么?
synchronized
方法是synchronized。
您不能禁止它。又是垃圾。不会出现这样的问题。服务器通过读取超时或写入异常甚至成功完成远程方法从这种情况中恢复。在所有三种情况下,方法都会退出,同步锁都会被释放,并且生命会继续。
废话。
除了您引用的摘录之外,我不知道这还可以指什么,而且我已经多次阅读该论文。如果作者想依赖这篇论文,他们应该提供引文以及对章节和诗节的正确引用。
无论如何,RMI 的设计者并没有做出这样的选择。没有这样的选择。
synchronized
是synchronized
,无论 RMI 设计者是否希望,类似地notify()
和wait()
> 是最终决定。
他们不能自由地做出任何选择。您提供的引用不是“选择”:它只是关于 Java 语义的声明。我认为你的理解是正确的,但它是完全错误的,而且不仅是错误的,而且明显是错误的。怎么可能是对的呢?
Java RMI 不会(而且确实不能)以任何方式改变、删除或扩展
同步
的语义。You are correct. The text is wrong. RMI stubs are thread-safe and can be invoked simultaneously by multiple threads within a single client JVM. I'm not aware of any statement or text by Wollrath et all that says anything different, and I've been following this topic since 1997.
Specifically:
You are correct.
The book is not only wrong, it is stating an impossibility. How exactly could RMI prevent synchronization from working?
Then blocking occurs at the server, by the normal operation of Java.
Rubbish. The fact that the remote method implementation is
synchronized
does everything that is necessary.Again this is rubbish.
Rubbish again.
'Allow'? What does this mean? A
synchronized
method issynchronized.
You can't disallow it.Again rubbish. No such problems arise. The server recovers from this situation either via a read timeout or an write exception or even successful completion of the remote method. In all three cases, the method exits, the synchronization lock is released, and life continues.
Nonsense.
I am not aware what else this could refer to other than the excerpt you quoted, and I've read that paper many times. If the authors want to rely on this paper they should have provided a quotation and a proper citation to chapter and verse.
In any case the designers of RMI made no such choice. There was no such choice to make.
synchronized
issynchronized
whatever the RMI designers may or may not have wished, and similarlynotify()
andwait()
arefinal.
They weren't free to make any choice. The quotation you provided isn't a 'choice': it is merely a statement about the semantics of Java.I think you're reading it correctly, and it's completely and utterly wrong, and not only wrong but obviously wrong. How could it possibly be right?
Java RMI doesn't, and indeed cannot, alter or remove or extend the semantics of
synchronized
in any way.