NIO和IO

什么是java的NIO

  • NIO(Non-Blocking I/O,java中,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,是解决高并发、I/O处理问题的有效方式。
  • 同步非阻塞,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求都会注册到多路复用器(Selector)上,多路复用器轮询到连接有 I/O 请求就进行处理。
  • 从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新特性,被统称为NIO(即New I/O)。新增了许多用于处理输入输出的类,这些类都被放在java.nio包及子包下,并且对原java.io包中的很多类进行改写,新增了满足NIO的功能。
  • NIO采用内存映射文件的方式来处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件了。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同, NIO支持面向缓冲区(Buffer)的、基于通道(Channel)的IO操作。NIO将以更加高效的方式进行文件的读写操作。

NIO中的三个重要组件:

  1. 缓冲区Buffer:缓冲区有直接缓冲区和非直接缓冲区之分,它实际上也是一段内存空间。在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的。缓冲区的实质是一个数组,通常来讲它是一个字节数组。流程如下图:
    在这里插入图片描述
  2. 通道Channel:Channel(通道)表示到实体如硬件设备、文件、网络套接字或可以执行一个或多个不同I/O操作的程序组件的开放的连接。
    Channel和传统IO中的Stream很相似。主要区别为:通道是双向的,通过一个Channel既可以进行读,也可以进行写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如InputStream只能进行读取操作,OutputStream只能进行写操作;通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
  3. 选择器Selector:Selector类是NIO的核心类,Selector(选择器)提供了选择已经就绪的任务的能力。Selector会不断的轮询注册在上面的所有channel,如果某个channel为读写等事件做好准备,那么就处于就绪状态,通过Selector可以不断轮询发现出就绪的channel,进行后续的IO操作。一个Selector能够同时轮询多个channel。这样,一个单独的线程就可以管理多个channel,从而管理多个网络连接。这样就不用为每一个连接都创建一个线程,同时也避免了多线程之间上下文切换导致的开销。

NIO&IO

核心区别:

  • NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的。所以在效率上的话,肯定是NIO效率比IO效率会高出很多。
  • NIO不在是和IO一样用OutputStream和InputStream 输入流的形式来进行处理数据的,但是又是基于这种流的形式,而是采用了通道和缓冲区的形式来进行处理数据的。
  • 还有一点就是NIO的通道是可以双向的,但是IO中的流只能是单向的。
  • 还有就是NIO的缓冲区(其实也就是一个字节数组)还可以进行分片,可以建立只读缓冲区、直接缓冲区和间接缓冲区,只读缓冲区很明显就是字面意思,直接缓冲区是为加快 I/O 速度,而以一种特殊的方式分配其内存的缓冲区。
  • 补充一点:NIO比传统的BIO核心区别就是,NIO采用的是多路复用的IO模型,普通的IO用的是阻塞的IO模型,两个之间的效率肯定是多路复用效率更高
  • 传统IO的弊端:传统IO就是工作的流程是当客户端发送消息的时候,会新建一个连接线程来处理连接,然后服务器端会有一个服务器线程(这个是无可避免的部分)来和客户端交互。但是我们可以这样想,当多并发连接的时候,会有很多个Thread,这样的线程无论客户端和服务器端是否进行通信都是存在的,会造成线程的开销很大。下面是一个展示的图,来展示一个BIO模式的工作流程:
    在这里插入图片描述
  • 如果是采用NIO的话,中间会多加一个选择器(其实可以认为是多路复用器),选择器会选择去轮询下面连接,如果发现下游有事件连接,可以判断连接的具体事件,然后对这个连接进行选择并且处理。这样可以实现用一个线程对多个连接进行维护,减少线程的开销。
    在这里插入图片描述
  • 当然,对于具体的业务,可能会出现有需要很多线程的情况,我们也可以建立多线程,这就是netty里面的group的思想,这里我就不展开讲了。
    在这里插入图片描述
<think>嗯,用户问的是NIOIO的区别,这得从Java的输入输出机制说起。首先,IO指的是传统的Java I/O,也就是阻塞式I/O,而NIO是New I/O,在Java 1.4引入的非阻塞式I/O。我需要先回忆这两个的基本概念,然后对比它们的不同点。 首先,IO是阻塞的,这意味着当一个线程执行读或写操作时,线程会被阻塞,直到数据准备好或者数据传输完成。这在处理多个连接时,可能需要为每个连接创建一个线程,导致资源消耗大。比如,用ServerSocketSocket的时候,每个客户端连接都会占用一个线程,如果连接数很高,线程数量会爆炸,影响性能。 而NIO是非阻塞的,它使用通道(Channel)缓冲区(Buffer)来进行数据传输,还有Selector机制。线程可以注册多个通道到Selector上,然后通过轮询Selector来检查哪些通道已经准备好进行I/O操作。这样单个线程就可以处理多个连接,提高了效率,适合高并发场景。 接下来是数据流的处理方式。IO是面向流的,意味着数据按顺序处理,不能随意移动指针。而NIO的Buffer是面向块的,数据被读入缓冲区后可以在其中前后移动,增加了灵活性。 然后是API的不同。IO提供的是InputStreamOutputStream这样的类,而NIO有Channel、Buffer、Selector这些组件。Selector是关键,允许单线程管理多个通道,实现非阻塞。 应用场景方面,IO适合连接数较少且数据传输量大的情况,比如文件处理。NIO适合需要处理大量并发连接,但每个连接数据量不大的情况,比如聊天服务器或者实时通信系统。 不过可能用户对阻塞非阻塞的具体表现不太清楚,需要举例子说明。比如,当使用IO的read()方法时,线程会一直等待数据到达,期间不能做其他事情。而NIO的通道配置成非阻塞模式后,调用read()会立即返回,不管有没有数据,线程可以继续处理其他通道。 另外,NIO的零拷贝特性可能也是优势之一,比如使用FileChannel.transferTo()方法,可以减少数据在用户空间内核空间的拷贝次数,提升性能。 需要检查是否遗漏了什么关键点,比如缓冲区的使用方式,事件驱动模型,或者NIO的复杂性更高,编程难度大。可能用户在实际选择时需要权衡易用性性能。 总结起来,区别主要在阻塞与非阻塞、流与块的处理方式、API结构以及适用场景。需要把这些点分清楚,用用户容易理解的语言解释,避免太技术化的术语,同时确保准确。</think>### NIOIO 的区别(Java 场景) #### 1. **基本模型** - **IO(阻塞式 I/O)**: 基于**流(Stream)**的同步阻塞模型。线程在读写数据时会被阻塞,直到操作完成。 例如:调用 `InputStream.read()` 时,线程会一直等待数据就绪。 - **NIO(非阻塞式 I/O)**: 基于**通道(Channel)****缓冲区(Buffer)**的同步非阻塞模型。通过 `Selector` 机制,单线程可监听多个通道的事件(如连接、读写),避免线程阻塞。 例如:线程通过轮询 `Selector` 检查哪些通道已就绪,再处理数据。 --- #### 2. **核心组件对比** | **组件** | **IO** | **NIO** | |-------------------|-------------------------------------|--------------------------------------| | 数据载体 | 流(单向:`InputStream/OutputStream`) | 通道(双向:`Channel`) + 缓冲区(`Buffer`) | | 阻塞性 | 阻塞(线程等待数据) | 非阻塞(线程可处理其他任务) | | 多路复用 | 不支持(需多线程) | 支持(通过 `Selector` 单线程管理多通道) | | 数据操作灵活性 | 流式顺序读写 | 缓冲区支持随机读写(通过 `position` 指针) | --- #### 3. **适用场景** - **IO 适用场景**: - 简单连接、低并发(如文件传输)。 - 代码简单直观,无需复杂事件驱动。 - **NIO 适用场景**: - 高并发、短连接(如即时通讯、聊天服务器)。 - 需要减少线程资源消耗的场景(如百万级连接)。 --- #### 4. **性能差异** - **IO 瓶颈**: 每个连接需独立线程处理,线程切换内存开销大。 公式:$$ \text{线程数} \propto \text{连接数} $$ - **NIO 优化**: 单线程通过 `Selector` 管理多通道,减少线程资源占用。 公式:$$ \text{线程数} \ll \text{连接数} $$ --- #### 5. **代码示例** - **IO 读取文件**: ```java try (FileInputStream fis = new FileInputStream("file.txt")) { int data; while ((data = fis.read()) != -1) { // 阻塞直到数据就绪 System.out.print((char) data); } } ``` - **NIO 读取文件**: ```java try (FileChannel channel = FileChannel.open(Paths.get("file.txt"))) { ByteBuffer buffer = ByteBuffer.allocate(1024); while (channel.read(buffer) != -1) { // 非阻塞,立即返回 buffer.flip(); while (buffer.hasRemaining()) { System.out.print((char) buffer.get()); } buffer.clear(); } } ``` --- #### 6. **总结** | **维度** | **IO** | **NIO** | |----------------|-----------------------|---------------------------| | 模型 | 阻塞 | 非阻塞 + 多路复用 | | 数据操作 | 单向流 | 双向通道 + 缓冲区随机访问 | | 线程效率 | 低(线程数=连接数) | 高(单线程处理多连接) | | 适用场景 | 低并发、大数据量 | 高并发、小数据包 | 选择建议: - 需要简单性 → **IO** - 需要高并发 → **NIO**(或更高级框架如 Netty)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值