java - 我真正需要多少个线程?

标签 java multithreading sockets user-interface

我正在构建一个带有 GUI 和基于套接字的服务器的 Java 应用程序,并且我经常遇到应用程序的一部分陷入等待另一部分的问题(主要是 GUI 等待服务器 - 不)令人惊讶的是。有几次我设法避免这些错误,但我发现自己在启动后几乎立即就到达了 main 方法的末尾。(应用程序可能会也可能不会继续运行,具体取决于是否有任何 GUI 是否可见,但我认为 main 方法在程序实际退出之前不应该返回...)

我对应用程序的要求如下:

  • 它应该能够同时处理未指定数量的客户端
  • 服务器和客户端之间的通信可以朝任一方向进行,而不必每隔一个方向进行;有时服务器会发送一堆消息并仅从某些客户端获得回复,有时则相反。
  • 客户端连接永远不应该“太晚” - 只要服务器应用正在运行,serversocket 就需要持续接受连接。
  • 在整个过程中,GUI 应该不受服务器和客户端相互等待的影响。 GUI 的更新通过其他对象(主要是模型)上的事件监听器进行,这些对象由后台线程更改。

我尝试过以下方法,但似乎无法正确执行。

  • 1 个线程用于 main 方法及其创建的对象( Controller 、模型等)执行的“常规”工作。这是我有时会遇到问题的线程,因为它保存在任何地方,并且过早地从main返回。
  • 使用 EventQueue.invokeLater(new Runnable() { ... }); 我在 UI 线程上执行所有实际的 GUI 操作,但这些调用都不是“存活”线程,因此它们基本上只是在主线程之外异步工作。
  • 1 个线程,让 ServerSocket 能够继续监听新连接。
  • 每个客户端有1个线程,能够监听来自客户端的消息。我不确定这里是否还需要另一个线程,以便能够“无序”发送消息,即无需先等待接收消息。

我以前从未编写过(真正的)多线程应用程序,所以这对我来说是全新的领域。然而,我不相信这个问题以前没有成功解决过——甚至多次出现某种最佳实践。

它们是什么?对于这个应用程序来说,什么是好的架构?

最佳答案

这个问题有很多不同的答案,但我能想到的最好规则是你需要一个 UI 线程(你没有说你在 GUI 中使用什么,但你提到了 invokeLater,所以我正在考虑 Swing),然后一个或多个线程用于处理客户端。不需要为每个客户端分配一个线程;使用java.nio而是使用异步 I/O 类。您可能希望使客户端处理线程的总数可以在运行时配置;范围会相当小,比如一到四。

运行应用程序的计算机(如果它确实是服务器)可能能够处理 4 个(例如,双核计算机)到 16 个(四核四核)实际并发执行线程(显然,有些服务器级机器的核心数量比这还要多,但您明白了),当然,您正在与架构上运行的所有其他服务共享这些机器。因此,拥有大量线程只会导致大量上下文切换。上下文切换很便宜,但远非免费,如果可以避免,那么 CPU 可以更有效地做其他事情。

有关使用 NIO 编码以最少线程处理大量客户端的服务器应用程序的示例,您可以查看 Netty 的源代码。事实上,您甚至可以考虑仅使用 Netty 并围绕其 I/O 处理构建应用程序逻辑。

<小时/>

旁注:

The app may or may not keep running, depending on if there is any GUI visible or not, but I though the main method wasn't supposed to return until the program is actually exiting...

main 一旦你让它结束就会结束。只要有未完成的正在运行的线程,JVM 就会继续运行。如果您希望 main 在退出之前等待其他线程,请使用 Thread#join加入他们。 join 导致当前线程等待,直到您调用 join 的线程终止(join 的某些重载提供超时,以便调用线程可以恢复如果被调用的线程在给定的时间内没有终止)。比较不带参数运行它与带参数运行它时的以下输出(任何参数,参数的内容并不重要):

public class JoinExample implements Runnable {

    public static final void main(String[] args) {
        Thread t = new Thread(new JoinExample());

        System.out.println("Starting thread");
        t.start();

        if (args.length > 0) {
            System.out.println("Joining thread");
            while (t.isAlive()) {
                try {
                    t.join();
                }
                catch (InterruptedException ie) {
                }
            }
        }

        System.out.println("main exiting");
    }

    public void run() {
        long    stop = System.currentTimeMillis() + 2000;

        System.out.println("Thread starting");
        while (System.currentTimeMillis() < stop) {
            // Sleep a mo
            try {
                Thread.currentThread().sleep(250);
            }
            catch (InterruptedException ie) {
            }
            System.out.println("Thread still running");
        }
        System.out.println("Thread stopping");
    }
}

综上所述,您可能希望让 main 线程终止,因为 UI 线程将是 Swing 创建的事件调度程序线程。有关线程和 swing 的更多信息 herehere .

关于java - 我真正需要多少个线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8778327/

相关文章:

java - 如何在java流的过滤器中包含结束日期

c - Unix pthreads 和信号 : per thread signal handlers

android 应用程序 socket.io 未连接到 node.js 服务器,但 android 浏览器建立连接

java - 后台线程中的 Progress bar.setProgress(variable)

java - JCE:验证 X509 自签名证书时出现异常

java - Android 空指针异常 - fragment (PA4AD)

java - 套接字与 SocketChannel

multithreading - Python 类管道流对象

java - 即使没有 dekker/peterson 的算法,Java 中的多线程也能工作吗?

c - 使用 icmp 套接字时是否应该将 id 写入 icmphdr id 字段?