BIO&NIO&AIO 悠悠 2022-11-14 13:29 178阅读 0赞 IO模型 java共支持3中IO模型:BIO,NIO,AIO。下面来总结一下三种模型。 # BIO # BIO(Blocking IO): 同步阻塞IO。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70][] ## 代码 ## public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(9000); System.out.println("BIOServer等待连接。。"); //阻塞方法 等待客户端的链接 Socket clientSocket = serverSocket.accept(); System.out.println("BIOServer有客户端连接了。。"); byte[] bytes = new byte[1024]; System.out.println("准备read。。"); //阻塞方法 等待客户端写数据,没有数据可读时就阻塞 int read = clientSocket.getInputStream().read(bytes); System.out.println("read完毕。。"); if (read != -1) { System.out.println("接收到客户端的数据:" + new String(bytes, 0, read)); } clientSocket.getOutputStream().write("HelloClient".getBytes()); clientSocket.getOutputStream().flush(); } 上述代码中,serverSocket.accept()和clientSocket.getInputStream().read(bytes)方法是阻塞的,如果多个客服端线程来连接服务端,就会造成阻塞,降低性能。如果使用多线程来解决性能问题,来多少个客户端开多少个线程,显然是不合适的,服务端压力也会加大。因此,BIO会有以下缺点: 1. IO代码里的accept和read操作是阻塞操作,会造成线程阻塞,浪费资源; 2. 如果线程过多,会导致服务端压力过大。 # NIO # NIO(Non Blocking IO): 同步非阻塞,从jdk1.4开始支持,主要有三大组件,channel,selector,buffer。 ## NIO三大组件: ## 1. channel(通道) 1. 类似于流,是双向的,一个流只可能是InputStream或是OutputStream,但是channel是双向的; 2. selector(选择器) 1. channel会注册到selector上,由selector根据channel的读写事件交给空闲的线程处理; 3. buffer(缓冲区) 1. 所有数据的读写都是通过buffer来进行的,java中的原生8种基本数据类型都有各自对应的buffer类型,(除Boolean外),如IntBuffer,CharBuffer,ByteBuffer,LongBuffer,ShortBuffer。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70 1][] ## 代码 ## public static void main(String[] args) { try { ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.configureBlocking(false); //设置非阻塞模式 serverSocketChannel.bind(new InetSocketAddress(9090));//绑定9090端口 Selector selector = Selector.open(); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //将selector注册到serverSocketChannel while (true) { selector.select();//此方法为阻塞方法 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> keyIterator = selectionKeys.iterator(); while (keyIterator.hasNext()) { SelectionKey selectionKey = keyIterator.next(); if (selectionKey.isValid()) { //此键是否有效 if (selectionKey.isAcceptable()) { //连接事件 ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) selectionKey.channel(); SocketChannel socketChannel = serverSocketChannel1.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); } if (selectionKey.isReadable()) { //读取数据 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); ByteBuffer buffer = ByteBuffer.allocate(1024); socketChannel.read(buffer); System.out.println(new String(buffer.array())); ByteBuffer buffer1 = ByteBuffer.wrap(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()).getBytes()); socketChannel.write(buffer1); } } keyIterator.remove();//移除key,避免重复消费 } } } catch (IOException e) { e.printStackTrace(); } } ## 步骤: ## 1. 开启ServerSocketChannel,绑定9090端口,并设置为非阻塞模式; 2. 开始Selector,并将ServerSocketChannel注册到Selector,监听连接事件; 3. 调用select()方法,是否有事件进来,如果有,则获取并遍历SelectionKey,判断其为连接事件还是读取事件,并做对应的操作,做完相应操作之后移除key,避免重复消费。 ## 总结 ## NIO是基于epoll基于事件响应机制来优化NIO。Redis也是典型的基于epoll的NIO线程模型(nginx也是),epoll实例收集所有事件(连接与读写事件),由一个服务端线程连续处理所有事件 命令,所以redis目前是单线程的。 # AIO # 异步非阻塞:适用于连接数目多且连接比较长(重操作)的架构。 ## 用法 ## AsynchronousServerSocketChannel与NIO中ServerSocketChannel用法基本一致,AsynchronousSocketChannel与SocketChannel用法基本一致,只不过是在CompletionHandler的回调completed方法中处理逻辑。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70]: /images/20221022/8df8fcde2b844a0ea6d90f6507b521b4.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjMwODEw_size_16_color_FFFFFF_t_70 1]: /images/20221022/1ad0dbb2ccbb40598e5dc41d7f9a98c0.png
还没有评论,来说两句吧...