Java RMI和线程同步问题

我实际上有两个关于Java RMI和线程同步的问题:

1)如果我将RMI远程方法实现为同步,它们是否保证互斥? 我需要确保没有两个我的RMI方法(提供给客户端的方法)同时执行。

2)我有一个服务器定期执行的方法。 它用于清理。 当远程客户端运行/使用任何RMI方法时,我必须确保不执行此特定方法。 此外,当该方法运行时,不应该进行RMI调用。 即客户必须等待。 知道我怎么能这样做吗? 我读过有关Locks的内容,但我不知道如何在这种情况下使用它们。

我已经考虑过将RMI方法实现为静态并在RMI接口中包含清理方法,但它似乎并不是解决问题的优雅方法。

我还将RMI接口内的清理方法编写为synchronized。 当我运行它进行测试时,方法之间似乎没有冲突,但我无法确定。

谢谢你的时间和答案。

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的静态方法可以锁定类。 使用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 + stub + synchronized,你会发现很多解释,比如这个:

Java RMI和同步方法

因此,您需要自己实现某种非RMI,纯粹的服务器端同步。 然后,您可以从远程方法调用此纯服务器端锁; 但是您需要额外的间接级别。

要测试代码,最简单的方法是将线程暂停在像Eclipse这样的好调试器下。 Eclipse将清楚地向您显示哪个暂停的线程持有哪个锁阻塞哪个其他线程。