
NIO基础概念NIO三大组件1. Channel1.1 基本概念1.2 常见的Channel:2. Buffer2.1 基本概念2.2 常见的Buffer3. Selector3.1 早期服务器端程序设计思路3.1.1 多线程3.1.2 线程池3.1.3 SelectorNIONon Blocking IO非阻塞IO也有人或教材理解为New IO三大组件1. Channel1.1 基本概念顾名思义是数据的传输通道Channel类似于Java BIO的【InputStream】和【OutputStream】是读写数据的通道可以从Channel将数据读入buffer也可以将buffer的数据写入Channel而Stream要么是输入InputStream要么是输出OutputStreamchannel比stream更为底层。Java IO中的输入流和输出流也是数据的传输通道只不过他们是单向的。输入流InputStream只能从文件或者网络中读取数据到程序输出流OutputStream只能从程序向文件或者网络中输出数据。而Channel是读写数据的双向通道。1.2 常见的Channel:FileChannel用于文件操作DatagramChannel专用于udp网络编程SocketChannel专用于tcpServerSocketChannel专用于tcp服务端2. Buffer2.1 基本概念从Channel中读取到的数据要先缓存到内存中Buffer就是这样的缓冲区用来暂存数据反之向Channel中写出数据之前也要将数据放到Buffer中。2.2 常见的Buffer最常用的是ByteBuffer3. Selectorselector字面意思为选择器选择什么呢不太好理解需要结合服务器端的代码设计来理解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 Selectorsocket、channel、connection这些都可以代表服务端和客户端的连接对象selector的作用就是配合【一个线程】来管理【多个channel】获取channel上发生的事件这些channel工作在【非阻塞模式】下不会让线程吊死在一个channel上。适合连接数多但是流量低的场景low trafficselector可以监测到所有channel上发生的事件当事件发生时就让线程处理事件。如果一个channel的处理时间很长 那么其他channel 得一直等待线程池版的话 即使socket不做任何事情 线程也得阻塞在这里什么也做不了 但是现在channel没事件线程就可以处理其他channel提高了线程的利用率。