这是一篇真的讲透了NIO原理的文章

先放上一个B站的视频:NIO的原理

说NIO就必须要了解下BIO,Java首先提供了BIO,为了优化BIO的性能短板,加入了NIO,搞清楚了BIO的短板,也就知道了为什么要有NIO。

BIO的B是blocking即阻塞,即在IO的过程中某些操作会导致线程阻塞,以Socket API为例,Server端:

 ServerSocket ss = new ServerSocket();
        InetSocketAddress ia = new InetSocketAddress("localhost", 8888);
        ss.bind(ia);
        while (true) {
    
    
            Socket socket = ss.accept();
            InputStream inputStream = socket.getInputStream();
            int read = inputStream.read();
            if (read == 0) {
    
    
                System.out.println("no data...");
            } else {
    
    
                System.out.println("has data...");
            }
        }

上面这段代码有两个阻塞处:

Socket socket = ss.accept();

int read = inputStream.read();

闭上眼睛,想象一下,你是一台服务器,执行到了 accept(),相当于站在家门口等待客人的到来,如果客人一直不来,你就要一直等在这里,你干不了其他事情。

等了半天,终于来了一个客人,你把它接入客厅,然后又开始等待,等待客人说话,客人不说话,你不知道该干什么,直到等到了客人说话,你才能干其他事情。

想象一下,如果客人一直不说话,而门口又来了很多客人需要你接待,就导致了服务器响应缓慢。

在想一下,如果客厅有了很多客人,而此时你恰好在门口等待客人,由于不等到客人就不能干其他事情,哪怕已经在客厅的客人有话要说,你也无暇顾及,你必须等到新客人才行。

好傻逼的主人。

BIO就是这样干的。

那NIO没出来之前,服务器不是都死慢死慢的?

那也不至于,刚刚举得例子是单线程的例子,主人既要迎宾又要陪客,的确难为他了。于是主人拿钱雇几个人去客厅帮忙接待客人,主人就可以专心致志的在门口迎接客人了。每次一个新客人到了之后,主人就分配一个雇员一对一接待,此时主人就得心应手的多了。

这时会有另一个问题,主人的财力有限,不能无限雇人,毕竟一个客人要一个员工去接待,很多客人其实么那么多要求,所以很多雇员绝大多数时间都很闲无事可干。

时间一长,主人觉得不行,客厅就那么大,站这么多雇员在里面,能接待接待客人的空间就少了。

这种情况下,BIO就搞不定了。

在实际的应用场景中,如果一个连接占用一个线程,会造成极大的浪费,按照28原则,80%的连接是不活跃的,80%的时间是不活跃的,这就意味着浪费了很多的资源。

NIO就是来解决这个问题的。

上面那个例子中,不要使用一个员工陪一个客户,让员工处理完一个客户的请求后,如果客户没有其他请求,则员工去看看其他的客人有没有什么要求,这样一个员工可以依次处理客人的需求。增加了客人的容量,同时使员工的工作更加饱和,不浪费员工的才华。

回过头再去想想,在没有雇员时,即不用多线程,BIO的场景有没有可以优化的余地,即主人既要迎宾又要待客时。即单线程下,如何优化?

先看一段伪代码:

public static void main(String[] args) throws IOException {
    
    
        ServerSocket ss = new ServerSocket();
        InetSocketAddress ia = new InetSocketAddress("localhost", 8888);
        ss.bind(ia);
        
        // 伪代码:设置accept不阻塞
        ss.setBlocking(false);
        List<Socket> sockets = new ArrayList<>();
        while (true) {
    
    
            
            // 此处不再阻塞
            Socket socket = ss.accept();
            InputStream inputStream = socket.getInputStream();
           
            if (socket == null) {
    
    
                System.out.println("no data...");
                for (Socket client : sockets) {
    
    
                    // 此处不再阻塞
                    int read = inputStream.read();
                }
            } else {
    
    
                
                // 伪代码:设置read()不阻塞
                socket.setBlocking(false);
                sockets.add(socket);
                System.out.println("has data...");

                for (Socket client : sockets) {
    
    
                    // 此处不再阻塞
                    int read = inputStream.read();
                }
            }
        }
    }

将原本阻塞的两个API设置为不阻塞,即主人先去门口迎客,没有客人时,并不做过多等待,而是跑会客厅接待客人,如果客人暂时没有要求,主人并不会傻等这个客人,而是马上去询问下一个客人,如果循环往复。这样主人能够做的事情就多了,资源得到充分利用,只不过主人的压力也就是随之升高。

NIO来了。

猜你喜欢

转载自blog.csdn.net/epitomizelu/article/details/121171434