Java RMI 和高级多线程

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

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

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

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

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

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

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


狐的传说
浏览 375回答 2
2回答

侃侃无极

概述这篇文章中似乎有一些问题,因此我将尝试详细介绍每个部分。问题 1 - 可以以任何方式控制多线程吗?是的!多线程的实现可以是任何你想要的。一个RMI的实现仅仅是有足够的抽象感觉到他们在1 JVM存在单独的JVM之间的通信; 因此对多线程没有影响,因为它只是通信层。问题 2 - 是为每个远程调用创建一个线程(在服务器端)还是使用线程池?请参阅此处的文档。如果它们在不同的线程上,简短的回答是否定的。由 RMI 运行时分派到远程对象实现的方法可能会或可能不会在单独的线程中执行。RMI 运行时不保证将远程对象调用映射到线程。由于对同一个远程对象的远程方法调用可能并发执行,因此远程对象实现需要确保其实现是线程安全的。使用线程池的RMI取决于实现,但作为使用RMI的开发人员,这应该无关紧要,因为它封装在RMI连接层中。问题 3 - 使用RMI,如何确保某些RMI调用等待其他调用终止?这是一个相当模糊的问题,但我认为您要问的是在RMI 中同步时如何正确阻止。这与您的应用程序设计有关。让我们假设您尝试访问数据库并且您必须同步数据库访问。如果客户端尝试通过RMI调用访问,它将调用远程服务器的方法来保存所有同步,因此如果必须的话等待锁定。因此,客户端将等待轮到它通过服务器访问数据库。因此,在您当前的场景中,您希望数据库同步出现在服务器端。问题 4 - 是否有另一种非 RMI 方法,具有相同的便利性和效率,可以更好地解决这个问题?绝对地。以下是可用于通信的通信实现的简要列表。1) RESTful2)RMI3) 插座4) gRPC我的建议是使用 RESTful,因为它是最直接的,并且在互联网上有大量的实现/文档。效率似乎对您来说非常重要,但您的操作只是以标准方式操作数据库。因此,我相信 Restful 实现将提供足够的效率。这样想;您有 N 个客户端、一个负载平衡器和 M 个服务器。客户端和服务器之间不存在持续连接,从而降低了复杂性和计算量。随着 N 个客户端的增长,负载均衡器会创建更多的服务器实例并适当地分配负载。请注意,客户端和服务器之间的请求实际上非常小,因为它们将具有有效负载和请求类型。此外,服务器将接收请求并正常并行计算操作。优化可以在服务器端通过线程池或 spring 等框架完成。

人到中年有点甜

您要求的是一种根据任务的依赖关系协调任务执行的方法。您的任务进行 RMI 调用这一事实无关紧要。我们可以想象一个纯粹的计算任务,它不访问远程机器并且仍然相互依赖,例如,通过提供在一个任务中计算的值作为其他任务的参数。依赖任务的协调是异步编程的核心问题。JDK对异步编程的支持并不全面,但足以解决你的问题。您需要使用两件事:CompletableFuture和一个Executor. 请注意,RMI 调用会阻塞它运行的线程,因此使用线程数有限的 Executor 会导致特定类型的死锁,称为“线程饥饿”,此时计算无法继续,因为所有可用线程都被阻塞。因此,使用具有无限数量线程的执行程序,最简单的是为每个任务创建新线程的执行程序:Executor newThreadExecutor = (Runnable r)->new Thread(r).start();然后,对于每个 RMI 调用(或任何其他任务),声明任务方法。如果任务不依赖于其他任务,则该方法应该没有参数。如果任务依赖于其他任务产生的结果,则声明一个或两个参数(CompletableFuture 不直接支持更多的参数)。让我们有:String m0a() {return "ok";}&nbsp; // no argsInteger m0b() {return 1;}&nbsp; // no argsDouble m2(String arg, Integer arg2) {return arg2/2.0;}&nbsp; // 2 args让我们要计算以下结果:String r0a = m0a();Integer r0b = m0b();Double r2 = m2(r0a, r0b);但异步,这样的呼吁m0a和m0b并行执行,并呼吁m2尽快启动双方m0a和m0b成品。然后,将每个任务方法用一个实例包裹起来,CompletableFuture根据任务方法的签名,使用不同的方法CompletableFuture:&nbsp;CompletableFuture<String> t0a = CompletableFuture.supplyAsync(this::m0a, newThreadExecutor)&nbsp;CompletableFuture<Integer> t0b = CompletableFuture.supplyAsync(this::m0b, newThreadExecutor)&nbsp;CompletableFuture<Double> t2 = t0a.thenCombineAsync(t0b, this::m2, newThreadExecutor)任务在声明后立即开始执行,不需要调用特殊start方法。要从最后一个任务中获得最终结果t2,可以使用get()接口方法Future:&nbsp;Double res = t2.get();
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java