NIO与原IO的区别
- 原来的IO和NIO最大的区别在于数据打包和传输的方式。原来的IO是基于流的方式处理数据,而NIO是基于块的方式处理数据。
- NIO适用于使用少数线程处理大量连接的情况
Java NIO的核心
- Channels
- Buffers
- Selectors
Java NIO通过事件驱动的方式监听消息,实际上是采用了Reactor模式。各部分的关系如图:
Channel
- 重要的实现
1. FileChannel:无法设置为非阻塞模式,总是运行在阻塞模式下
2. DatagramChannel:收发UDP数据包的通道
3. SocketChannel:TCP连接客户端通道,可以设置为非阻塞
4. ServerSocketChannel:TCP连接服务端通道,可以设置为非阻塞 - 特点
1. 既可以从通道中读数据到缓冲区,也可以从缓冲区中写数据到通道;通道支持异步的读写
2. 支持Buffer的Scatter和Gather操作
Buffer
- 重要的实现
1. 八种中基本数据类型的Buffer
2. MappedByteBuf,用于表示内存映射文件,直接内存方式,速度比较快 - 特点
1. Buffer的读、写模式是相互对立的,进行反转需要调用flip()方法
2. Buffer的工作原理:capacity,limit,position,需要了解读和写情况下的意义
Selectors
- 一个Selector能够检测多个Channel
- Channel需要在Selector上注册自己感兴趣的事件,事件主要有:Connect,Accept,Read,Write
- 调用Selector的select方法阻塞等待Channel上的特定事件就绪
- select()返回准备就绪的通道个数,调用selector.selectedKeys()返回已经准备就绪的事件
- 在Selector的open方法中,会根据不同的操作系统提供不同的内部实现,如果是Linux操作系统,则使用Poll(kernel < 2.6)、epoll(kernel > 2.6);如果是window操作系统,则是select,如果是Unix,则是kqueue
Selector的唤醒底层实现
Selector在open()的时候,会建立一个Pipe,通过Pipe实现唤醒。具体参考《Nio Selector阻塞唤醒原理》
1. Window下不支持使用Pipe,所以通过两个连接的SocketChannel实现了Pipe,通过本地地址127.0.0.1自己连接自己
2. Linux下则直接使用系统的Pipe进行通信。
Java NIO的缺点
- 需要自己处理半包和粘包问题。当从通道中读数据到缓冲区的时候,我们并不知道缓冲区中的内容是否是一个完整的包,需要自己手动的进行处理。
- 这些问题可以通过Netty进行解决