我在Java中实现了服务器,在从某个客户端接收数据时,它只是将数据转发给所有其他客户端(包括发送方(。我对我的 OO 设计很满意,我将所有套接字包装在提供"回调"的类中。当某些数据准备就绪(或套接字关闭时(会调用这些数据包 - 使用这种设计,我可以轻松地实现一个简单的TLV协议来原子地发送数据包:在收到完整的数据包之前不会调用回调。
现在,我使用 java.io
包阻止对套接字流的 I/O 调用(并通过这些回调使它们看起来"异步"(。所以我在我的套接字包装类中使用线程:当一个套接字被打开时,该函数返回一个Runnable
实现,当运行时,将对InputStream
进行阻塞调用,缓冲数据并最终调用回调。
=> 在客户端应用程序中,我只是在Thread
实例中启动此Runnable
,因为它只是一个线程。
=> 在我的服务器中,我将创建新套接字(即接受新客户端时(获得的所有Runnable
实现提交到ThreadPoolExecutor
中。(仅供参考:套接字的回调只是put
BlockingQueue
中接收到的数据包。单个单独的(非池化("调度程序"Thread
实例不断take
此队列中的数据包,并将它们写入当前连接到服务器的所有套接字。
问题:这一切都很好用,但是我不确定我是否使用ThreadPoolExecutor
,因为提交的Runnable
实例几乎总是阻塞。ThreadPoolExecutor
会对此做出反应吗?还是池化线程会简单地阻塞?因为,如果所有池化线程在执行其Runnable
时都阻塞,接下来提交一个新的Runnable
,那该怎么办?暂停新Runnable
?这不好,因为这样新连接的客户端的响应能力将为零,直到某些较旧的客户端断开连接。如果相比之下,线程池选择生成一个新线程来处理Runnable
,那么我实际上得到了一个每个客户端的线程方案。
我希望线程池"抢占"阻塞线程并使用它们来处理其他套接字,例如暂停 I/O 绑定进程的操作系统,并且在它们的 I/O 完成之前不会再次调度它们。这是否可能,或者我必须使用 nio
重写所有内容才能做到这一点?(如果需要nio
,你能指出我应该从哪里开始阅读吗?
提前感谢!
关于ThreadPoolExecutor
:这取决于。Executors.newCachedThreadPool()
只会为新的 Runnable 创建新线程。另请参阅此问题和接受的答案。但最终会得到每个客户端的线程方案。
Nio
防止每个客户端的线程方案(如果有许多客户端发送相对较小的消息,中间有暂停,另请参阅本文(摘要(,我建议不要尝试构建自己的nio
克隆。
实现nio
并不容易,可以在此处找到教程。使用像Netty这样的nio
服务器可能更容易。
另一种选择是使用旨在处理许多发送和接收小消息的客户端的技术。学习和设置需要一些时间,但我设法让Tomcat WebSockets服务器与Jetty WebSocket客户端快速通信。重写以使用此技术可能会减少工作量。