我真的需要多少个线程?

时间:2012-01-08 14:28:03

标签: java multithreading sockets user-interface

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

我对申请的要求如下:

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

我尝试了以下内容,但我似乎无法做到正确。

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

我之前从未编写过(真正的)多线程应用程序,所以这对我来说是全新的。但是,我拒绝相信这个问题以前没有成功解决 - 甚至很多次,某些最佳实践已经发展。

他们是什么?这个应用程序的优秀架构是什么?

1 个答案:

答案 0 :(得分:1)

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

运行应用程序的机器,如果它真的是服务器,可能能够处理四个(例如,双核双核机器)到十六(四核四核)实际并发执行线程(显然有服务器级别的机器拥有更多内核,但你明白了),当然,你正在与架构上运行的所有其他服务共享这些机器。因此,拥有大量线程只会导致大量的上下文切换。上下文切换很便宜,但远没有自由,如果可以避免,那么CPU可以更有用地做其他事情。

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


旁注:

  

应用程序可能会也可能不会继续运行,具体取决于是否有任何GUI可见,但我认为主要方法不应该返回,直到程序实际退出...

只要你让它结束,

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创建的事件调度程序线程。有关线程和摆动的更多信息herehere