NIO基础概念
NIO
Non Blocking IO,非阻塞IO;
也有人或教材理解为New IO;
三大组件
1. Channel
1.1 基本概念
顾名思义,是数据的传输通道;
-
Channel类似于Java BIO的【InputStream】和【OutputStream】,是读写数据的通道,可以从Channel将数据读入buffer,也可以将buffer的数据写入Channel,而Stream要么是输入(InputStream),要么是输出(OutputStream),channel比stream更为底层。
-
Java IO中的输入流和输出流,也是数据的传输通道,只不过他们是单向的。输入流InputStream只能从文件或者网络中读取数据到程序,输出流OutputStream只能从程序向文件或者网络中输出数据。而Channel是读写数据的双向通道。
1.2 常见的Channel:
FileChannel,用于文件操作;
DatagramChannel,专用于udp网络编程;
SocketChannel,专用于tcp;
ServerSocketChannel,专用于tcp服务端;
2. Buffer
2.1 基本概念
从Channel中读取到的数据,要先缓存到内存中,Buffer就是这样的缓冲区,用来暂存数据;反之,向Channel中写出数据之前,也要将数据放到Buffer中。
2.2 常见的Buffer
最常用的是ByteBuffer

3. Selector
selector字面意思为选择器,选择什么呢?不太好理解,需要结合服务器端的代码设计来理解selector。
在java nio这套API出现之前,服务器端程序是怎样设计的呢?
3.1 早期服务器端程序设计思路
3.1.1 多线程
服务器要处理与多个客户端的通信,一个客户端,在代码层面,表现为一个Socket。
客户端一旦与服务器建立连接,就会创建Socket对象,将来两端就通过这个Socket进行数据读写(通过java IO输入输出流)。
每个Socket,服务器会专门启动新的线程来处理。如果有多个客户端,那么服务器就要创建多个线程,每个线程专门处理一个Socket。
这种思路的缺点:
- 连接数少的时候,这种思路没问题;但是如果有大批量的客户端连接上来,就需要创建大量线程;线程本身就会占用一定的资源,如果线程数过多,就会占用过多内存资源;
- 线程虽然有很多,但是cpu核心是有限的;线程内的代码最终还是要靠cpu执行。
比如cpu有16个核心,那么真正能同时执行的只有16个线程,其他的线程不得不等待。
线程等待的时候,会保存线程当前的状态。当轮到这些线程执行的时候,就会进行线程上下文切换。上下文切换过于频繁,效率也不会很高。
因此,该方案仅适合连接数少的场景。
3.1.2 线程池
第一种思路的主要问题是:线程数一旦增多,就会导致内存资源占用过多,线程上下文切换成本变高。那么是否可以用线程池来限制线程的数量呢?
Java Socket API工作在【阻塞模式】,阻塞模式下,线程同一时间只能处理一个Socket连接的读写操作。比如一个客户端连接到服务器,那么线程开始处理与该客户端的Socket读写操作,在该客户端断开连接之前,线程都只能处理该客户端的Socket,哪怕此时该Socket已经没有读写操作发生,线程也只能干等着,不能去处理其他客户端的Socket。
这就是线程池版的局限性。所以早期的服务器都是设计成短连接,客户端连上之后,做完一项业务处理,赶紧把连接断开,让线程去处理下一个客户端。
早期的tomcat,就是线程池+阻塞IO,适合HTTP 1.0。
因此,该方案仅适合短连接的场景。
3.1.3 Selector
socket、channel、connection,这些都可以代表服务端和客户端的连接对象;

-
selector的作用就是配合【一个线程】来管理【多个channel】,获取channel上发生的事件,这些channel工作在【非阻塞模式】下,不会让线程吊死在一个channel上。适合连接数多,但是流量低的场景(low traffic)
-
selector可以监测到所有channel上发生的事件,当事件发生时,就让线程处理事件。
-
如果一个channel的处理时间很长 那么其他channel 得一直等待
-
线程池版的话 即使socket不做任何事情 线程也得阻塞在这里什么也做不了 但是现在channel没事件线程就可以处理其他channel,提高了线程的利用率。
792



被折叠的 条评论
为什么被折叠?



