BIO、NIO、AIO 的区别
BIO
包括基于字节流的 InputStream 和 OutputStream,以及基于字符流的 Reader 和Writer。 ## NIO NonBlock-IO:构建多路复用的、同步非阻塞的 IO 操作 - Channels 其中 FileChannels 有 transferTo 和 transferFrom 两个方法,适合大文件的拷贝,避免了两次用户态和内核态的上下文切换,即"零拷贝",效率高
- Buffers
 
- Selectors 允许单线程处理多个 Channels。底层调用的是系统级别的 select
 使用单线程的轮询事件的机制,可以高效定位到就绪的 channel,只有 select 阶段是阻塞的,可以避免大量客户端连接时频繁切换线程的开销。 
select、poll 和 epoll 的区别
- select 单个进程所能够打开的最大连接数由 FD_SETSIZE 宏定义,大小是 32 个整数的大小(在 32 为机器上,大小是3232,64 位机器上,大小是 3264),我们可以对其进行修改,然后重新编译内核,但是性能无法保证,需要做进一步测试
 
- poll 本质上与 select 没有区别,大师它没有最大连接数的限制,因为它是基于链表来存储的
 
- epoll 虽然连接数有上限,但是很大,1G 内存可以打开 10 万左右的连接
 
FD 剧增后带来的 IO 效率问题
消息传递方式
AIO
Asynchronous IO,基于事件和回调机制,异步非阻塞 - 基于回调:实现 Completionhandler 接口,调用时触发回调函数 - 返回 Future:通过 isDone() 查看是否准备好,通过 get() 等待返回数据
BIO 例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | public class BIOPlainEchoServer {     public void serve(int port) throws IOException {         //将 ServerSocket 绑定到指定的端口         final ServerSocket socket = new ServerSocket(port);         while (true) {             //阻塞直到收到客户端的连接             final Socket clientSocket = socket.accept();             System.out.println("Accepted connection from " + clientSocket);             //创建一个子线程去处理客户端的请求             new Thread(new Runnable() {                 @Override                 public void run() {                     try {                         BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));                         PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());                         //从客户端读取数据并原封不动写回去                         while (true) {                             writer.println(reader.readLine());                             writer.flush();                         }                     } catch (IOException e) {                         e.printStackTrace();                     }                 }             }).start();
          }     }
      public void improvedServe(int port) throws IOException {         //将 ServerSocket 绑定到指定的端口         final ServerSocket socket = new ServerSocket(port);         //创建一个线程池         ExecutorService service = Executors.newFixedThreadPool(6);         while (true) {             //阻塞直到收到客户端的连接             final Socket clientSocket = socket.accept();             System.out.println("Accepted connection from " + clientSocket);             service.execute(() -> {                 try {                     BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));                     PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());                     //从客户端读取数据并原封不动写回去                     while (true) {                         writer.println(reader.readLine());                         writer.flush();                     }                 } catch (IOException e) {                     e.printStackTrace();                 }             });
          }     } }
  | 
 
NIO 例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | public class NIOPlainEchoServer {
      public void serve(int port) throws IOException {         System.out.println("Listening for connection on port: " + port);         ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();         ServerSocket ss = serverSocketChannel.socket();         InetSocketAddress address = new InetSocketAddress(port);         //将 ServerSocket 绑定到指定的端口         ss.bind(address);         serverSocketChannel.configureBlocking(false);         Selector selector = Selector.open();         //将 channel 注册到 selector 里,并说明 selector 关注的点,这里是关注建立连接这个事件         serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);         while (true) {             //阻塞等待就绪的 channel,即没有与客户端建立连接前就一直轮询             selector.select();             //获取到selector里所有就绪的 SelectionKey 实例,每将一个 channel 注册到 selector 就会产生一个 SelectionKey             Set<SelectionKey> readyKeys = selector.selectedKeys();             Iterator<SelectionKey> iterator = readyKeys.iterator();             while (iterator.hasNext()) {                 SelectionKey key = iterator.next();                 //将就绪的 SelectionKey 从 selector 中移除,因为马上就要去处理它,防止重复执行                 iterator.remove();
                  //若 SelectionKey 处于 Acceptable 状态                 if (key.isAcceptable()) {                     ServerSocketChannel server = (ServerSocketChannel) key.channel();                     SocketChannel clientChannel = server.accept();                     System.out.println("Accepted connection from " + clientChannel);                     clientChannel.configureBlocking(false);                     //向 selector 注册 clientChannel,主要关注读写事件,并传入一个ByteBuffer实例供读写缓存                     clientChannel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, ByteBuffer.allocate(100));                 }
                  //若 SelectionKey 处于可读状态                 if (key.isReadable()) {                     SocketChannel clientChannel = (SocketChannel) key.channel();                     ByteBuffer out = (ByteBuffer) key.attachment();                     //从 channel 里读取数据存入 ByteBuffer                     clientChannel.read(out);                 }
                  //若 SelectionKey 处于可写状态                 if (key.isWritable()) {                     SocketChannel clientChannel = (SocketChannel) key.channel();                     ByteBuffer out = (ByteBuffer) key.attachment();                     out.flip();                     //将 ByteBuffer 的数据写入 channel                     clientChannel.write(out);                     out.compact();                 }             }         }     } }
  | 
 
BIO、NIO、AIO 对比
| blocking | 
阻塞并同步 | 
非阻塞但同步 | 
非阻塞并异步 | 
| 线程数(server:client) | 
1:1 | 
1:N | 
0:N | 
| 复杂度 | 
简单 | 
较复杂 | 
复杂 | 
| 吞吐量 | 
低 | 
高 | 
高 |