【问题标题】:Java RMI and advanced multithreadingJava RMI 和高级多线程
【发布时间】:2019-02-01 06:35:19
【问题描述】:

我正在实现类似于数据库的东西,其中数据操作语句(插入、更新和删除)得到评估。有些语句可以同时执行,而另一些则不能(我计算得出)。我喜欢 RMI 的易用性和便利性,但是我需要更深入地了解 RMI 服务实现 w.r.t 多线程。例如,

  1. 可以以任何方式控制多线程吗?

  2. 是为每个远程调用创建一个线程(在服务器端)还是使用了线程池?

  3. 更一般地说,使用 RMI,如何确保某些 rmi 调用等待其他调用终止?

  4. 是否有另一种非 RMI 方法,具有相同的便利性和效率,可以更好地解决这个问题?

  5. 如果我想要多线程,我应该自己在服务器端代码上创建线程吗?担心的是,如果 RMI 服务创建了多个线程,那么我将添加额外的不必要的线程。

例如,如果每次调用都创建一个线程,那么我可以使用 java join 方法来命令语句执行。另一方面,如果使用线程池,则 join 方法将不起作用(因为线程不会终止)。

【问题讨论】:

  • RMI 是一个通信层,本质上与线程无关。每次调用是否使用新线程或池线程完全取决于 RMI 实现。我认为参考实现是前者。无论如何,像java.util.concurrent 这样好的线程池实现会通知您线程任务完成,通常通过ExecutableFuture 接口或类似接口。最后但并非最不重要的一点是,我建议避免使用 RMI,因为它是一种非常过时的技术,而是使用更现代的东西,例如 RESTful Web 服务。
  • @sparc_spread 如果 RMI 没有指定或允许控制服务器端的多线程,那么它似乎不适合上面的 DML 应用程序,因为我想要并发。换句话说,同时处理调用很重要。
  • 如果你想控制并发,控制它。使用synchronized 关键字或信号量。没有奥秘。在最简单的层面上,将您的远程方法设为synchronized
  • 1.没有。 2.这与你的问题无关。 3. 同上。 4. 不,它已经是多线程的了。您所要做的就是确保在服务器端进行适当的同步。 RMI 的单线程实现在理论上是可能的,但它完全没用,而且它们不存在。

标签: java multithreading rmi


【解决方案1】:

概述

这篇文章中似乎有一些问题,所以我将尝试详细介绍每个部分。

问题 1 - 可以以任何方式控制多线程吗?

是的!多线程的实现可以是任何你想要的。 RMI 实现只是独立 JVM 之间的通信,具有足够的抽象性,让人感觉它们存在于 1 个 JVM 上;因此对多线程没有影响,因为它只是通信层。

问题 2 - 是为每个远程调用创建一个线程(在服务器端)还是使用线程池?

请参阅文档here。如果它们在不同的线程上,简短的回答是否定的。

由 RMI 运行时分派给远程对象实现的方法可能会也可能不会在单独的线程中执行。 RMI 运行时不保证将远程对象调用映射到线程。由于对同一个远程对象的远程方法调用可能同时执行,远程对象的实现需要确保其实现是线程安全的。

RMI 使用线程池取决于实现,但作为使用 RMI 的开发人员,这应该是无关紧要的,因为它被封装在 RMI 连接层。

问题 3 - 使用 RMI,如何确保某些 RMI 调用等待其他调用终止?

这是一个相当模糊的问题,但我认为您要问的是在 RMI 中同步时如何正确阻止。这与您的应用程序设计有关。让我们假设您尝试访问数据库并且您必须同步 DB 访问。如果客户端试图通过 RMI 调用访问,它将调用远程服务器的方法来保存所有同步,因此如果必须等待锁定。因此,客户端将等待轮到它通过服务器访问DB。因此,在您当前的情况下,您希望 DB 的同步出现在 服务器端

问题 4 - 是否有另一种非 RMI 方法,具有相同的便利性和效率,可以更好地解决这个问题?

当然。下面是可用于通信的通信实现的简要列表。

1) RESTful

2) RMI

3) 套接字

4) gRPC

我的建议是使用 RESTful,因为它是最直接的并且在 Internet 上有大量的实现/文档。效率似乎是您非常关心的问题,但您的操作只是以标准方式操作数据库。因此,我相信 Restful 实现会提供足够多的效率。

这样想;你有 N 个客户端、一个负载均衡器和 M 个服务器。客户端和服务器之间不存在恒定的连接,从而降低了复杂性和计算量。随着 N 个客户端的增长,负载均衡器会创建更多的服务器实例并适当地分配负载。请注意,客户端和服务器之间的请求实际上非常小,因为它们将具有有效负载和请求类型。此外,服务器将接收请求并正常并行计算操作。可以通过线程池或spring等框架在服务器端进行优化。

【讨论】:

  • 似乎为了提高效率,我想在服务器端创建多个线程。但是如果 RMI 实现没有指定或允许对其进行控制,那么它就不适用于上面讨论的 DML 应用程序。
  • 关于其他方法:RestFul - 似乎更适合使用 HTTP 的基于 Web 的应用程序。不确定它是否足够有效。套接字:级别太低,尽管它允许对线程进行更多控制。不确定 gRPC。它的效率与 RMI 相比如何?
  • 记住,尝试将 RMI 与多线程分开。它们是两个不同的东西。仅仅因为底层实现是一回事,并不意味着您的应用程序不能使用线程池/多线程/等。但是,如果您希望直接控制 RMI 实现,则不能。
  • 所以你建议我自己在服务器端创建线程?如果 rmi 服务器已经这样做了,那不是多余的吗?
  • RMI 处理连接。如果您担心一次连接太多,那么 RMI 可能不是解决方案。如果您为了最小化服务器端的计算时间而使用多线程,那么实现自己的线程就可以了,而且不是多余的。 RMI并没有减少计算时间,它是2个JVM之间的连接实现,因此为了优化的多线程可以结合它来完成。
【解决方案2】:

您要求的是一种根据任务依赖关系协调任务执行的方法。您的任务调用 RMI 的事实是微不足道的。我们可以想象一个纯计算任务,它不访问远程机器并且仍然相互依赖,例如,通过将在一个任务中计算的值作为其他任务的参数提供。

依赖任务的协调是异步编程的核心问题。 JDK 对异步编程的支持并不全面,但足以解决您的问题。您需要使用 2 个东西:CompletableFutureExecutor。请注意,RMI 调用会阻塞它运行的线程,因此使用线程数有限的 Executor 可能会导致特定类型的死锁,称为“线程饥饿”,因为所有可用线程都被阻塞而无法继续计算。所以使用线程数不限的执行器,最简单的是为每个任务创建新线程的执行器:

Executor newThreadExecutor = (Runnable r)->new Thread(r).start();

然后,对于每个 RMI 调用(或任何其他任务),声明任务方法。如果任务不依赖于其他任务,则该方法应该没有参数。如果任务依赖于其他任务产生的结果,则声明一个或两个参数(CompletableFuture 不直接支持更多的参数)。让我们有:

String m0a() {return "ok";}  // no args
Integer m0b() {return 1;}  // no args
Double m2(String arg, Integer arg2) {return arg2/2.0;}  // 2 args

让我们要计算以下结果:

String r0a = m0a();
Integer r0b = m0b();
Double r2 = m2(r0a, r0b);

但是是异步的,因此对m0am0b 的调用是并行执行的,并且在m0am0b 完成后立即开始对m2 的调用。

然后,用CompletableFuture的实例包装每个任务方法,根据任务方法的签名,使用CompletableFuture的不同方法:

 CompletableFuture<String> t0a = CompletableFuture.supplyAsync(this::m0a, newThreadExecutor)
 CompletableFuture<Integer> t0b = CompletableFuture.supplyAsync(this::m0b, newThreadExecutor)
 CompletableFuture<Double> t2 = t0a.thenCombineAsync(t0b, this::m2, newThreadExecutor)

任务在声明后立即开始执行,不需要调用特殊的start方法。

要得到最后一个任务t2的最终结果,可以使用接口Future的方法get()

 Double res = t2.get();

【讨论】:

  • 总的来说,我正在寻找解决 DML 问题的最佳方法。我并不是真的在问如何协调依赖关系,因为我知道如何做到这一点。一般来说,由于我想同时执行 DML 语句,我想在单独的线程中执行每个语句。想象一下 RMI 是这样实现的,每次对远程对象的调用都在一个新线程(或从线程池中获得的线程)中执行。在这种情况下,我可能不需要自己生成线程。理想情况下,在我看来,rmi 应该像 http 服务器一样支持多线程。
  • @danb 什么是“DML 问题”?
  • 这是我在问题的第一个陈述中引用的内容。 DML 代表数据操作语句。
  • @danb 我在第一条语句中看不到任何问题描述。我在第二个语句中看到了类似的内容,并描述了我的解决方案如何使一些语句同时执行而其他语句按顺序执行。即首先假设所有任务并发执行,并在不应该并发执行的任务之间添加依赖关系。我提供了一个执行器,其中“对远程对象的每次调用都在一个新线程中执行”。也就是说,我仍然无法理解您要解决的问题。
  • 假设 RMI 实现是单线程的。那么每个通话怎么可能不阻塞其他通话呢?
猜你喜欢
  • 2014-06-13
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-18
  • 2013-01-28
  • 1970-01-01
相关资源
最近更新 更多