我正在使用GUI和基于套接字的服务器构建Java应用程序,并且我经常遇到问题,应用程序的一部分卡在等待另一部分(主要是GUI等待服务器 - 没有在那里,我设法避免这些错误几次,我发现自己在启动后几乎立即到达main
方法的末尾。(应用程序可能会也可能不会继续运行,具体取决于是否有任何GUI可见或不可见,但我虽然main
方法不应该返回,直到程序实际退出...)
我对申请的要求如下:
我尝试了以下内容,但我似乎无法做到正确。
main
方法的1个线程和由它创建的对象执行的“常规”工作(Controller,Model等)。这是我有时会遇到问题的线程,因为它不会保留在任何地方并且过早地从main
返回。EventQueue.invokeLater(new Runnable() { ... });
我在UI线程上执行所有实际的GUI操作,但这些调用都不是“幸存”线程,因此它们基本上只是在主线程上异步工作。ServerSocket
能够继续侦听新连接。我之前从未编写过(真正的)多线程应用程序,所以这对我来说是全新的。但是,我拒绝相信这个问题以前没有成功解决 - 甚至很多次,某些最佳实践已经发展。
他们是什么?这个应用程序的优秀架构是什么?
答案 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创建的事件调度程序线程。有关线程和摆动的更多信息here和here。