【问题标题】:Java RMI and Thread Synchronization questionsJava RMI 和线程同步问题
【发布时间】:2011-01-17 13:35:35
【问题描述】:

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

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

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

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

我还在 RMI 接口中编写了同步的清理方法。当我运行它进行测试时,方法之间似乎没有冲突,但我不能确定。

感谢您的时间和回答。

【问题讨论】:

  • 如果 ewernli 或我发布的任何内容对您有帮助,请至少给个赞 ;-)

标签: java multithreading rmi synchronized


【解决方案1】:

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 的“常规”方法会获取对象实例上的锁。这些不是相同的隐式锁!

但是如果你只实例化了一个远程对象,你可以将lock设为静态(这与锁定类相同,但更简洁一些)。清理代码也可以是静态的,并且与远程对象在同一个类中。

骨架:

public class MyRemoteClass {
   public static Object lock = new Object();

   public void doStuff()
   {
       synchronized( lock ) { ... }
   }
}

public class Cleanup {
   public static void doIt()
   {
       synchronized( MyRemoteClass.lock ) { ... }
   }
}

【讨论】:

  • 我不知道否决票。无论如何,您能详细说明一下锁定机制吗?我在另一个回复中看到了一个参考,我并没有完全理解它。如果我在远程对象中实现锁,如何从服务器本地对象获取锁?远程对象中的公共静态变量之类的东西?或者,如果您可以提供任何链接/教程/代码 sn-ps,也可以。谢谢。 :)
  • 'RMI 不提供此类保证。'没错,但这不是问题所在。他正在同步。同步确实提供了这种保证。
  • 同步所有方法的效果与我们获得的证据上的锁定对象一样。
  • 这就是我需要确认的 EJP。如果我将 RMI 接口中的所有方法都实现为同步,我能确定它们中的任何一个都不会在任何时候同时运行吗?
  • @EJP 感谢您的解释。我已经编辑了我的答案。希望现在好多了。
【解决方案2】:
  1. 对于来自 RMI 客户端的每个调用,RMI 服务器将在新线程中执行调用。您只需要同步对共享对象的访问即可。

  2. 另一个线程或计时器不会阻止您的服务器接受来自客户端的调用。这需要同步,最佳实践取决于清理作业运行多长时间可以中断,或者是否可以将请求放入队列等。 最简单的方法是让 RMI 方法等待 ewernli 已经描述的锁。

编辑:根据您的评论,一个骨架演示了如何实现这种基本同步。由于现在一切都是相互排斥的,因此您不能期望涉及多个客户的高性能。无论如何,这将满足您的要求。 (我希望)。如果您的项目增长,您应该阅读Concurrency Tutorial

Object mutex = new Object();

int rmiMethod1() {
    synchronized (mutex) {
        doWhatNeeded1();
    }
}

int rmiMethod2() {
    synchronized (mutex) {
        doWhatNeeded2();
    }
}

// in your cleanup thread
void run() {
    synchronized (mutex) {
        cleanUp();
    }
}

【讨论】:

  • 我知道 RMI 在新线程中执行新调用。问题是我不希望两个远程方法同时运行。这包括不多次调用相同的方法。我认为只需使用同步方法就可以解决问题。我通过搜索谷歌从某个论坛上读到了这一点。 RMI 是否肯定会忽略同步指令并继续同时运行远程方法?
  • 感谢您提供这个非常说明性的示例。关于互斥对象。同步(互斥):这条线做什么?它是否获得了为对象提供的锁?另外,为什么不使用并发教程中所示的 Lock 对象?谢谢。
  • 同步(互斥)试图获取一个所谓的监视器,以确保只有一个线程进入关键(受保护)部分。如果另一个线程已经进入临界区,所有其他线程都必须等到它离开同步 {} 块。本教程中描述了执行此操作的可能方法。这是我个人的喜好。
  • (1) 不正确。首先,RMI 的线程行为是有意且明确地没有指定的。其次,在 Sun 实现中,连接池可能意味着来自同一个客户端的连续调用在同一个线程中执行。展示的代码相当于只是同步了相关的方法,会简单很多。
  • @EJP 嗯,多年后我又读了一遍。我可能试图描述我在必须不时维护的系统中观察到的行为。
【解决方案3】:

您必须记住,RMI 创建了“远程对象”的假象,但实际上至少有三个对象:本地存根、远程骨架和实际的远程对象。作为设计权衡,这种错觉是完整的,并且锁定本地存根。整个网络没有同步。在网上搜索 RMI+stub+synchronized,你会发现很多解释,比如这个:

Java RMI and synchronized methods

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

要测试您的代码,最简单的方法是在 Eclipse 等良好的调试器下暂停您的线程。 Eclipse 会清楚地告诉你哪个暂停的线程持有哪个锁阻塞了其他线程。

【讨论】:

  • 自 1998 年以来就没有骷髅了。
  • 您的意思是“从 Java 1998 开始”吗?更严重的是:是的,编译时骨架已被运行时等效物替换。它与此页面有任何相关性吗?我对此表示怀疑。
  • 我的意思是我写的,如果那里有一个笑话我不明白。如果它与这个答案无关,你应该从这个答案中删除它。其余的都很好。
  • what '只锁定本地存根'?
【解决方案4】:

澄清所有这些困惑:

  1. 如果您同步远程方法实现,一次只能执行一个客户端。

  2. 如果您在它加入的远程对象上同步清理任务 (1)。

  3. 您不能将远程方法定义为静态的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多