美文网首页
十一 BIO NIO

十一 BIO NIO

作者: BeYearn | 来源:发表于2018-11-22 20:20 被阅读0次
    1. 首先,需要澄清一些基本概念:
    • 区分同步或异步(synchronous/asynchronous)。简单来说,同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调用返回,才会进行下一步;而异步则相反,其他任务不需要等待当前调用返回,通常依靠事件、回调等机制来实现任务间次序关系。

    • 区分阻塞与非阻塞(blocking/non-blocking)。在进行阻塞操作时,当前线程会处于阻塞状态,无法从事其他任务,只有当条件就绪才能继续,比如 ServerSocket 新连接建立完毕,或数据读取、写入操作完成;而非阻塞则是不管 IO 操作是否结束,直接返回,相应操作在后台继续处理。

    1. BIO
      IO 都是同步阻塞模式,所以需要多线程以实现多任务处理
    public class BioServer extends Thread {
        private ServerSocket serverSocket;
        private ExecutorService executor;
        
        public int getPort() {
            return serverSocket.getLocalPort();
        }
    
        public void run() {
            try {
                executor = Executors.newFixedThreadPool(8); //初始化一个线程池来执行
                serverSocket = new ServerSocket(0);
                while (true) {
                    Socket socket = serverSocket.accept();
                    RequestHandler requestHandler = new RequestHandler(socket);
                    executor.execute(requestHandler);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (serverSocket != null) {
                    try {
                        serverSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    // 简化实现,不做读取,直接发送字符串
    class RequestHandler extends Thread {
        private Socket socket;
        RequestHandler(Socket socket) {
            this.socket = socket;
        }
        @Override
        public void run() {
            try (PrintWriter out = new PrintWriter(socket.getOutputStream())) {
                out.println("Hello world!");
                out.flush();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
            DemoServer server = new DemoServer();
            server.start();
            try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) {
                BufferedReader bufferedReader = new BufferedReader(new                   InputStreamReader(client.getInputStream()));
                bufferedReader.lines().forEach(s -> System.out.println(s));
            }
        }
     }
    
    1. NIO1
      利用了单线程轮询事件的机制,通过高效地定位就绪的 Channel,来决定做什么,仅仅 select 阶段是阻塞的,可以有效避免大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高
    public class NioServer extends Thread {
        public void run() {
            try (Selector selector = Selector.open();
                 ServerSocketChannel serverSocket = ServerSocketChannel.open()) {// 创建 Selector 和 Channel
                serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
                serverSocket.configureBlocking(false);
                // 注册到 Selector,并说明关注点
                serverSocket.register(selector, SelectionKey.OP_ACCEPT);
                while (true) {
                    selector.select();// 阻塞等待就绪的 Channel,这是关键点之一 当有 Channel 发生接入请求,就会被唤醒。
                    Set<SelectionKey> selectedKeys = selector.selectedKeys();
                    Iterator<SelectionKey> iter = selectedKeys.iterator();
                    while (iter.hasNext()) {
                        SelectionKey key = iter.next();
                        // 生产系统中一般会额外进行就绪状态检查
                        sayHelloWorld((ServerSocketChannel) key.channel());
                        iter.remove();
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void sayHelloWorld(ServerSocketChannel server) throws IOException {
            try (SocketChannel client = server.accept();) {
                //通过 SocketChannel 和 Buffer 进行数据操作 发送一段字符串
                client.write(Charset.defaultCharset().encode("Hello world!"));
            }
        }
        // 省略了与前面类似的 main
    }
    
    1. NIO2 (java7引入, 或叫AIO)
      之后介绍

    2. 由于nio实际上是同步非阻塞io,是一个线程在同步的进行事件处理,当一组事channel处理完毕以后,去检查有没有又可以处理的channel。这也就是同步+非阻塞。同步,指每个准备好的channel处理是依次进行的,非阻塞,是指线程不会傻傻的等待读。只有当channel准备好后,才会进行。
      那么就会有这样一个问题,当每个channel所进行的都是耗时操作时,由于是同步操作,就会积压很多channel任务,从而完成影响。那么就需要对nio进行类似负载均衡的操作,如用线程池去进行管理读写,将channel分给其他的线程去执行,这样既充分利用了每一个线程,又不至于都堆积在一个线程中,等待执行.

    B和N通常是针对数据是否就绪的处理方式来区别
    sync和async是对阻塞进行更深一层次的阐释,区别在于数据拷贝由用户线程完成还是内核完成,讨论范围一定是两个线程及以上了。

    同步阻塞,从数据是否准备就绪到数据拷贝都是由用户线程完成 bio
    同步非阻塞,数据是否准备就绪由内核判断,数据拷贝还是用户线程完成 nio
    异步非阻塞,数据是否准备就绪到数据拷贝都是内核来完成 aio

    所以真正的异步IO一定是非阻塞的。

    多路复用IO即使有Reactor通知用户线程也是同步IO范畴,因为数据拷贝期间仍然是用户线程完成。
    详细可参考 https://tech.meituan.com/nio.html?utm_source=tool.lu

    相关文章

      网友评论

          本文标题:十一 BIO NIO

          本文链接:https://www.haomeiwen.com/subject/yvjlqqtx.html