什么';Java中异步文件NIO的好处是什么?

什么';Java中异步文件NIO的好处是什么?,java,asynchronous,nio,java-io,Java,Asynchronous,Nio,Java Io,根据和的文档,异步NIO使用一个专用的线程池,其中“处理IO事件”。我找不到任何关于“处理”在这个上下文中的含义的明确表述,但根据我的理解,我非常确定最终,阻塞会发生在那些专用线程上。为了缩小范围,我使用Linux,根据答案,Linux上没有非阻塞IO,只有Windows在某些级别上支持它 我的问题是:使用异步NIO与在我自己创建的专用线程池上运行同步IO相比,有什么好处?考虑到引入的复杂性,什么样的场景仍然值得实现呢?主要是关于手动调整缓冲区大小。这样,您可以节省大量内存,但前提是您要同时处理

根据和的文档,异步NIO使用一个专用的线程池,其中“处理IO事件”。我找不到任何关于“处理”在这个上下文中的含义的明确表述,但根据我的理解,我非常确定最终,阻塞会发生在那些专用线程上。为了缩小范围,我使用Linux,根据答案,Linux上没有非阻塞IO,只有Windows在某些级别上支持它


我的问题是:使用异步NIO与在我自己创建的专用线程池上运行同步IO相比,有什么好处?考虑到引入的复杂性,什么样的场景仍然值得实现呢?

主要是关于手动调整缓冲区大小。这样,您可以节省大量内存,但前提是您要同时处理大量(数千)个连接

首先是一些简化和警告:

  • 我将假设一个非愚蠢的调度程序。有些操作系统在处理数千个线程方面做得很差。当用户进程触发1000个完整线程时,操作系统不会崩溃,这并没有内在的原因,但有些操作系统无论如何都会崩溃。NIO可以在这方面提供帮助,但这有点不公平——通常你应该升级你的操作系统。几乎所有的linux,我相信win10肯定不会有这么多线程的问题,但是ARM上的一些旧linux端口,或者类似windows 7的东西,可能会导致问题

  • 我假设您正在使用NIO处理传入的TCP/IP连接(例如web服务器或IRC服务器之类的)。如果您试图同时读取1000个文件,同样的原则也适用,但请注意,您确实需要考虑瓶颈所在。例如,从一个磁盘同时读取1000个文件是一个毫无意义的练习——这只会减慢速度,因为你会使磁盘的使用变得更困难(如果磁盘正在旋转,这会加倍计算)。对于网络,尤其是如果您使用的是快速管道,瓶颈不是管道或网卡,这使得“同时处理1000个连接”成为一个很好的例子。事实上,我将以一个聊天服务器为例,其中1000人全部连接到一个巨大的聊天室。这项工作是接收任何有联系的人发来的短信,并发送给每个人

同步模型 在同步模型中,生命相对简单:我们将创建2001个线程:

  • 1个线程侦听套接字上的新传入TCP连接。此线程将创建2个“处理程序”线程,并返回侦听新连接
  • 每个用户一个线程,从套接字读取数据,直到看到enter符号。如果它看到这一点,它将接收到目前为止收到的所有文本,并用这个需要发送的新字符串通知所有1000个“发送者”线程
  • 每个用户一个线程,它将在“要发送的文本消息”缓冲区中发送字符串。如果没有什么东西可以发送,它将等待新消息发送给它
每个单独的移动件都很容易编程。在战术上使用单个
java.util.concurrent
数据类型,甚至一些基本
synchronized()
块将确保我们不会遇到任何竞争条件。我设想每件作品可能有一页代码

但是,我们有2001个线程。每个线程都有一个堆栈。在JVM中,每个线程都有相同大小的堆栈(不能创建线程,但堆栈大小不同),并且可以使用
-Xss
参数配置它的大小。你可以让它们小到128k,但即使这样,也仍然是
128k*2001
=~256MB,只是对于堆栈而言,我们还没有涵盖任何堆(人们来回发送的所有字符串,卡在发送队列中),或者应用程序本身,或者JVM基础知识

在引擎盖下,有16个内核的CPU将会发生什么,比如说,有2001个线程,每个线程都有自己的一组条件,这将导致它被唤醒。对于接收者来说,它的数据通过管道进入,对于发送者来说,它要么是网卡,表明它已经准备好发送另一个数据包(以防它正在等待将数据推下线路),要么是等待
obj.wait()
调用以获得通知(从用户接收文本的线程会将该字符串添加到1000个发送者的所有队列中,然后通知所有发送者)

这是大量的上下文切换:一个线程醒来,看到
乔:大家好,早上好!
在缓冲区中,将其转换为一个数据包,然后将其放入网卡的内存缓冲区(这一切都非常快,只是CPU和内存的交互作用),然后CPU内核将继续运行,并找到另一个准备好做一些工作的线程

CPU核心有核心上的缓存;事实上,有一个层次结构。有主RAM,然后是三级缓存、二级缓存、核心上的缓存——在现代体系结构中,CPU不能再在RAM上运行了,它们需要芯片周围的基础设施来实现它需要读取或写入不在这些缓存中的页面上的内存,不是吗然后CPU将冻结一段时间,直到infra可以将该页RAM复制到其中一个缓存中

每次核心切换时,很可能需要加载一个新页面,这可能需要数百个周期,而CPU在这一过程中无所事事。一个写得不好的调度程序会导致比需要多得多的问题。如果你读到NIO的优点,通常会出现“那些上下文切换很昂贵!”的情况——这是或多或少的少了他们所说的(但是,剧透警告:异步模型也会受到影响!)

异步模型 在同步模型中,计算1000个连接用户中的哪一个已经准备好进行测试的工作
// synchronous code
int size = readInt();
byte[] buffer = new byte[size];
int pos = 0;
while (pos < size) {
    int r = input.read(buffer, pos, size - pos);
    if (r == -1) throw new IOException("Client hung up");
    pos += r;
}
sendMessage(username + ": " + new String(buffer, StandardCharsets.UTF_8));