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