验证BIO NIO AIO三种IO模型的区别 | Java Debug 笔记

234 阅读4分钟

一 前言

随着学习的加深,乐乐渐渐通过撸代码能更好的帮助理解学习于此同时也查询到一些资料

二 IO模型优势对比

nio对应bio有哪些优势和好处,最明显的就是传统的bio, 是一个服务端socket和一个客户端socket建立直连,并且服务端需要为每个客户端新起一个线程去处理客户端的通讯交互,这样必然会无故的开销服务端的很多资源.

而我们的nio只需要一个选择器和一个轮询线程就能接入成千上万甚至更多的客户端连接,这点是nio相比bio最大的进步和改变.其次就是建立连接的方式,传统的bio是通过客户端服务端三次握手的方式建立tcp连接,而nio是客户端直接把通道注册到服务端的多路复用器上,然后服务端去轮询,这就减少了三次握手请求响应的开销。再次之就是缓冲区代码直连流,传统的bio请求和响应数据读是通过一端创建输出流直接向另一端输出,而另一点穿件输入流写入数据,这样就很依赖网络,如果网络不好就会导致流长时间不能关闭,从而导致资源无故浪费,增加开销.而nio引入了缓冲区都数据写数据都是直接向缓冲区读写,这样就不依赖网络,一端吧数据写完到缓冲区就可以关闭写入流,这时候只需要通知另一端去读。另一端开启读取流快速的读取缓冲区的数据,然后就可以快速的关闭.如果网络不好情况向就不会开销另一端的资源。

三 代码实践

  1. BIO
  • 服务端

       try {
           serverSocket = new ServerSocket();
           serverSocket.bind(new InetSocketAddress(7397));
           System.out.println("itstack-demo-netty bio server start done.");
           while (true) {
               Socket socket = serverSocket.accept();
               BioServerHandler handler = new BioServerHandler(socket, Charset.forName("GBK"));
               handler.start();
           }
       } catch (IOException e) {
           e.printStackTrace();
       }
    
  • 客户端

       try {
           Socket socket = new Socket("127.0.0.1", 7397);
           System.out.println("itstack-demo-netty bio client start done.");
           BioClientHandler bioClientHandler = new BioClientHandler(socket, Charset.forName("utf-8"));
           bioClientHandler.start();
       } catch (IOException e) {
           e.printStackTrace();
       }
       
    

从代码上来看就是很简单,一对一的直连,先创建在绑定在接受三步走,但是多个连接过来就要开启多个线程

2.NIO

  • 服务端

     try {
          //打开多路复用器
          selector = Selector.open();
          //打开服务器通道
          socketChannel = ServerSocketChannel.open();
          //设置通道为非阻塞
          socketChannel.configureBlocking(false);
          //绑定地址
          socketChannel.socket().bind(new InetSocketAddress(port), 1024);
          //把服务器通道注册到多路复用器上,并且监听阻塞事件
          socketChannel.register(selector, SelectionKey.OP_ACCEPT);
          System.out.println("itstack-demo-netty nio server start done.");
          new NioServerHandler(selector, Charset.forName("GBK")).start();
      } catch (IOException e) {
          e.printStackTrace();
      }
    
  • 客户端

       Selector selector = Selector.open();
       SocketChannel socketChannel = SocketChannel.open();
       socketChannel.configureBlocking(false);
    
       boolean isConnect = socketChannel.connect(new InetSocketAddress("192.168.1.116", 7397));
       if (isConnect) {
           socketChannel.register(selector, SelectionKey.OP_READ);
       } else {
           socketChannel.register(selector, SelectionKey.OP_CONNECT);
       }
       System.out.println("itstack-demo-netty nio client start done. ");
       new NioClientHandler(selector, Charset.forName("GBK")).start();
       
    

    现在就更容易理解了。之前BIO是直连的 现在NIO搞出来一个中间层,有点像云盘,把服务端要上传的数据都先上传到这里面,不用等到有连接了再上传,上传完了就不管了,但是BIO就得管。

3.AIO

  • 服务端

        try {
          serverSocketChannel = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withCachedThreadPool(Executors.newCachedThreadPool(), 10));
          serverSocketChannel.bind(new InetSocketAddress(7397));
          System.out.println("itstack-demo-netty aio server start done. ");
          // 等待
          CountDownLatch latch = new CountDownLatch(1);
          serverSocketChannel.accept(this, new AioServerChannelInitializer());
          latch.await();
      } catch (Exception e) {
          e.printStackTrace();
      }
      
    

    先说说里面用到的CountDownLatch类,这个类是计数器,如果有多个线程同时访问一个资源要怎么控制了,他就能帮我们完成控制。他的功能就是,必须等到其他想成执行完,才能执行他下面的资源,所以把自己初始化成1,然后他就await 这个await就是在等待他的资源执行完,这样才能执行下一个。

  • 客户端

       AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();
       Future<Void> future = socketChannel.connect(new InetSocketAddress("127.0.0.1", 7397));
       System.out.println("itstack-demo-netty aio client start done.");
       future.get();
       socketChannel.read(ByteBuffer.allocate(1024), null, new AioClientHandler(socketChannel, Charset.forName("GBK")));
       Thread.sleep(100000);
       
    

    客户端使用到了Future,这个Future是啥呢,他是非阻塞的,也就是用了他不用等返回结果,就可以执行下面的功能,但是如果我要返回结果呢,那就要执行future.get()这个方法就是会或得返回的结果,这个类还是很有特色的,能同能异。AIO是异步非阻塞,他的非阻塞就体现在Future.