关于NIO 的概述,和Channel通道 讲解

本文详细介绍了Java NIO的Channel和Buffer概念,包括阻塞与非阻塞IO的区别,NIO的核心组件如Selector、Channel和Buffer的使用方法。重点讲解了FileChannel的各种操作,如读写、截取、定位和强制写入等,同时也探讨了SocketChannel和DatagramChannel在非阻塞模式下的应用。通过实例展示了Scatter/Gather的读写操作,阐述了NIO如何提高IO效率和处理大量连接的能力。
摘要由CSDN通过智能技术生成

Java NIO 概述


Java NIO(New IO 或 Non Blocking IO)是从Java 1.4 版本开始引入的一个新的IO API,可以替代标准的 Java IO API。NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式进行文件的读写操作。

1.1 阻塞 IO


通常在进行同步 I/O 操作时,如果读取数据,代码会阻塞直至有可供读取的数据。同样,写入调用将会阻塞直至数据能够写入。传统的 Server/Client 模式会基于 TPR(Thread per Request),服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的一个问题就是线程数量的剧增,大量的线程会增大服务器的开销。大多数的实现为了避免这个问题,都采用了线程池模型,并设置线程池线程的最大数量,这由带来了新的问题,如果线程池中有 100 个线程,而有100 个用户都在进行大文件下载,会导致第 101 个用户的请求无法及时处理,即便第101 个用户只想请求一个几 KB 大小的页面。传统的 Server/Client 模式如下图所示:

 

如果线程数量不够别的请求使用,那其他的请求就会被阻塞,等待拿到线程去处理;

1.2 非阻塞 IO(NIO)


通过Selector去实时监听通道中的事件

NIO 中非阻塞 I/O 采用了基于 Reactor 模式的工作方式,I/O 调用不会被阻塞,相反是注册感兴趣的特定 I/O 事件,如可读数据到达,新的套接字连接等等,在发生特定事件时,系统再通知我们。NIO 中实现非阻塞 I/O 的核心对象就是 Selector,Selector 就是注册各种 I/O 事件地方,而且当我们感兴趣的事件发生时,就是这个对象告诉我们所发生的事件,如下图所示:

 

从图中可以看出,当有读或写等任何注册的事件发生时,可以从 Selector 中获得相应的 SelectionKey,同时从 SelectionKey 中可以找到发生的事件和该事件所发生的具体的 SelectableChannel,以获得客户端发送过来的数据。

非阻塞指的是 IO 事件本身不阻塞,但是获取 IO 事件的 select()方法是需要阻塞等待的.区别是阻塞的 IO 会阻塞在 IO 操作上, NIO 阻塞在事件获取上,没有事件就没有 IO, 从高层次看 IO 就不阻塞了.也就是说只有 IO 已经发生那么我们才评估 IO 是否阻塞,但是select()阻塞的时候 IO 还没有发生,何谈 IO 的阻塞呢?

NIO 的本质是延迟 IO 操作到真正发生 IO 的时候,而不是以前的只要 IO 流打开了就一直等待 IO 操作。

 

1.3 NIO 概述


Java NIO 由以下几个核心部分组成:

  • Channels
  • Buffers
  • Selectors

虽然 Java NIO 中除此之外还有很多类和组件,但 Channel,Buffer 和 Selector 构成了核心的 API。其它组件,如 Pipe 和 FileLock,只不过是与三个核心组件共同使用的工具类。

1.3.1 Channel


首先说一下 Channel,可以翻译成“通道”。Channel 和 IO 中的 Stream(流)是差不多一个等级的。

只不过 Stream 是单向的,譬如:InputStream, OutputStream.

Channel 是双向的,既可以用来进行读操作,又可以用来进行写操作。

NIO 中的 Channel 的主要实现有:

  • FileChannel     文件
  • DatagramChannel     UDP
  • SocketChannel          TCP
  • ServerSocketChannel           

这里看名字就可以猜出个所以然来:分别可以对应文件 IO、UDP 和 TCP(Server 和 Client)。

1.3.2 Buffer


NIO 中的关键 Buffer 实现有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer,IntBuffer, LongBuffer, ShortBuffer,分别对应基本数据类型: byte, char, double,float, int, long, short。

1.3.3 Selector


一个Selector可以监听多个channel,如果channel中有他关心的事件发生,就可以通过SelectionKey获取到内容,最终完成操作

Selector 运行单线程处理多个 Channel,如果你的应用打开了多个通道,但每个连接的流量都很低,使用 Selector 就会很方便。例如在一个聊天服务器中。要使用Selector, 得向 Selector 注册 Channel,然后调用它的 select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新的连接进来、数据接收等。


2.1 Channel 概述


Channel 是一个通道,可以通过它读取和写入数据,它就像水管一样,网络数据通过Channel 读取和写入。

通道与流的不同之处在于通道是双向的,流只是在一个方向上移动(一个流必须是 InputStream 或者OutputStream 的子类),而且通道可以用于读、写或者同时用于读写。

因为 Channel 是全双工的,所以它可以比流更好地映射底层操作系统的 API。

NIO 中通过 channel 封装了对数据源的操作通过 channel 我们可以操作数据源,但又不必关心数据源的具体物理结构。这个数据源可能是多种的。比如,可以是文件,也可以是网络 socket。在大多数应用中,channel 与文件描述符或者 socket 是一一对应的。

Channel 用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据。

channel 接口源码

 
 

Channel 是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

Java NIO 的通道类似流,但又有些不同:

既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
通道可以异步地读写。
通道中的数据总是要先读到一个 Buffer,或者总是要从一个 Buffer 中写入。
正如上面所说,从通道读取数据到缓冲区,从缓冲区写入数据到通道。如下图所示:

 

 

2.2 Channel 实现


下面是 Java NIO 中最重要的 Channel 的实现:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是小晴晴呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值