2011年的Java:线程套接字VS NIO:在64位操作系统和最新的Java版本上可以选择什么?

我在StackOverflow和一些博客上阅读了几篇关于java.net vs java.nio的post。 但我仍然无法理解何时应该更喜欢NIO而不是螺纹sockets。 你能否在下面检查我的结论并告诉我哪些不正确以及哪些错过了?

  • 因为在线程模型中,您需要为每个活动连接专用一个线程,并且每个线程为其堆栈提供大约250千字节的内存,使用每插槽线程模型,您将在大量并发连接上快速耗尽内存。 与NIO不同。

  • 在现代操作系统和处理器中,可以认为大量活动线程和上下文切换时间对于性能几乎无关紧要

  • NIO的整个吞吐量可能更低,因为异步NIO库在高负载环境中使用的select()和poll()比唤醒和放入hibernate线程更昂贵。

  • NIO一直比较慢,但它允许您处理更多的并发连接。 它本质上是一个时间/空间权衡:传统IO速度更快但内存占用更大,NIO速度更慢但使用的资源更少。

  • Java每个并发线程的硬件限制为15000/30000,具体取决于JVM,这会将每个连接模型的线程限制为此并发连接数最大,但JVM7将没有此限制(无法确认此数据)。

所以,作为结论,你可以这样:

  • 如果你有数万个并发连接 – 除非请求处理速度是你的关键因素,否则NIO是更好的选择
  • 如果你的数量少于 – 每个连接的线程是一个更好的选择(假设你可以负担大量的RAM来保持所有并发线程的堆栈达到最大值)
  • 使用Java 7,您可能希望在任何一种情况下都使用NIO 2.0。

我对么?

这对我来说似乎是正确的,除了关于Java限制线程数量的部分 – 通常受其运行的操作系统的限制(请参阅Java VM支持多少线程?并且无法 通过Java 获取2542个线程) 4GB iMac OSX 10.6.3 Snow Leopard(32bit) )。

要达到那么multithreading,您可能需要调整JVM的堆栈大小。

我仍然认为传统IO中线程的上下文切换开销很大。 在较高级别,如果他们不会争用相同的资源,或者他们花费的时间远远高于资源上下文切换开销,那么您只能使用多个线程获得性能。 实现这一目标的原因在于SSD等新的存储技术,您的线程可以更快地回击CPU

没有一种“最佳”的方式来构建NIO服务器,但这个特定问题在SO上的优势表明人们认为存在! 您的问题总结了适合这两个选项的用例,以帮助您做出适合您的决策。

此外,混合解决方案也是可能的! 当他们要做一些值得付出代价的事情时,你可以将频道交给线程,并且当它更好时坚持使用NIO。

我会说从每个连接线程开始,如果遇到问题就从那里进行调整。

如果你真的需要处理一百万个连接,你应该考虑在C(或其他)中编写(或找到)一个简单的请求代理,每个连接使用的内存远远少于任何java实现。 代理可以异步接收请求,并将它们排队到后端用您选择的语言编写的工作程序。

因此,后端每个活动请求只需要一个线程,并且您可以只有一个固定数量的线程,因此在某种程度上预先确定了内存和数据库的使用。 当大量请求并行运行时,请求会等待一段时间。

因此,我认为您永远不必在64位系统上使用NIO选择通道或异步I / O(NIO 2)。 每个连接线程模型运行良好,您可以使用一些更合适的低级技术扩展到“数十或数十万”连接。

避免过早优化(即在真正有大量连接进入之前编写NIO代码)并且如果可能的话不重新发明轮子(Jetty,nginx等)总是有帮助的。

最常被忽视的是NIO允许零拷贝处理。 例如,如果您使用单个服务器上的旧学校套接字从多个进程内侦听相同的多播流量,则会将任何多播数据包从网络/内核缓冲区复制到每个侦听应用程序。 因此,如果您构建例如20个进程的GRID,则会出现内存带宽问题。 使用nio,您可以检查传入缓冲区,而无需将其复制到应用程序空间。 然后,该进程仅复制其感兴趣的部分传入流量。

另一个应用程序示例:请参阅http://www.ibm.com/developerworks/java/library/j-zerocopy/以获取示例。