前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第一次听人用男女关系讲 N(Non-Blocking)I(进)O(出),涨姿势了

第一次听人用男女关系讲 N(Non-Blocking)I(进)O(出),涨姿势了

作者头像
好好学java
发布2021-04-19 12:51:38
5950
发布2021-04-19 12:51:38
举报
代码语言:javascript
复制

BIO:80 年代屌丝追妹

80 年代屌丝男买了一个 BP 机用来追妹,男士使用传呼台给女生留言:

男:下午一起看个电影?[早晨 10 点]

这是男生唯一心动的女生,所以一直守着自己的 BP 机,等待女生回复,就这样一天过去了,直到:

男:BP 没电,自动关机。

名词解释

  1. BP 机和传呼台指的是 BIO 中的流单向传输的特性,屌丝男士通过传呼台给 BP 机发送消息是单向的,如果是女生通过传呼台回复屌丝男士,也是单向的。
  2. 这个女生是男士唯一心动的女生,所以他傻等着 BP 机回复,即便可能一直不会有消息,这就是同步阻塞 IO,中 B 的概念。

BIO 的缺点

  1. 同步阻塞 IO,如果存在多个请求的时候,服务端必须通过多线程处理,增加了服务端的压力和创建销毁线程的开销。
  2. 如果连接一直没有响应,服务端也需要一直监听端口等待,浪费了服务端资源。

NIO:80 年代公子哥把妹

80 年代的公子哥买了一个大哥大,关键是这个公子哥太花心,同时中意了两个妹子,于是他就开始了把妹过程。 公子哥用大哥大给女 A 打电话:

公子哥 -> 女 A:下午一起看个电影?[早晨 10 点] 女 A -> 公子哥:我正在吃饭,你晚点再打过来?

公子哥用大哥大给女 B 打电话:

公子哥 -> 女 B:下午一起看个电影?[早晨 10 点 1 分] 女 B -> 公子哥:我正在吃饭,你晚点再打过来?

过了 10 分钟公子哥再次打电话询问

公子哥 -> 女 A:怎么样,有空吗?[早晨 10 点 10 分] 女 A -> 公子哥:我下午陪爸爸打马球,不去了。 公子哥 -> 女 B:怎么样,有空吗?[早晨 10 点 11 分] 女 B -> 公子哥:好呀,下午 3 点来我家接我吧。 公子哥 -> 女 B:好嘞,我开车去接你。

最终公子哥成功了追求到了女 B,这个故事告诉我们,成功的前提是有钱。(你怎么看?)

名词解释

  1. 公子哥用上了大哥大,可以实现双向的通话,这就是 NIO 中的 Channel,可以实现双向的数据流传输。
  2. 公子哥不用像 BP 机小哥一样死等着回复,每次打电话都能得到回复,挂断电话一会儿再来询问即可,这就是 Channel 的非阻塞特性,也就是 “N” 的体现。
  3. 公子哥可以同时撩两个妹子,这就是 NIO 的 IO 多路复用,也就是 Selector。
  4. 公子哥只能同时和一个人通话,这就是同步,所以 NIO 的全称叫做同步非阻塞 IO。

优缺点

  1. 非阻塞 IO 模型,不需要阻塞在特定的请求。
  2. 一个线程可以处理多个请求,不需要客户端和服务器端一比一的对应,没有多线程创建和销毁带来的系统开销。
  3. 服务端不需要死等请求,减少了服务端压力。

关键名词

  1. Channel,双向传输,非阻塞的通道,有FileChannel,DatagramChannel,ServerSocketChannel/SocketChannel 等。
  2. Buffer,数据块的读写,可以理解字节数组,效率高。四个重要属性:capacity 容量,position 位置, limit 上限,用户切换读写时候的游标,mark 标记,标记 position 的位置,分为堆内内存和堆外内存,也是 NIO 性能的关键内容。
  3. Selector,IO 多路复用的关键,实现了循环查看可以使用的 Channel,解决死等问题。
  4. Selector 实现有多种方式,自己写一个数组循环也是方式,也可以实现 IO 多路复用,只是性能好坏而已,所以基于底层 poll、select和epoll 也是实现“遍历”可用通道的方式不同而已。select 使用轮询的方式,有 1024 个连接的限制,poll 去掉了这个限制依然是轮询的方式,epoll 是基于系统的注册回调的方式,监听系统的事件实现。
  5. NIO 引入了 Buffer 的概念,每次使用 Buffer 拷贝数据其实是一次从用户空间(JVM) 向系统空间(系统内存) 的一次拷贝, Java 里面提供了 DirectByteBuffer 堆外内存,如果使用使用堆外内存,可以减少一次系统空间和用户空间的拷贝,这种现象叫做零拷贝。强调一下,并不是操作系统不能直接操作 HeapByteBuffer(对内内存),而且在 GC 的作用下,内存地址可能随时变化,操作的内存数据不一定准备。
  6. IO 多路复用性能更好,针对的 I/O 密集型应用程序,如果是 CPU 密集型应用程序,还是通过多线程的方案。所以很多写 IO 多路复用的文章都会说“多线程的创建,必然存在创建销毁和切换的开销,在高并发系统中,会拖慢整个系统”,其实并不是非常的准确,虽然是想说明 I/O 多路复用的利好,但是确实有点以偏概全。

AIO:21 世纪非智能时代大学生把妹

21 世纪初期,还没有智能机,不过诺基亚 1110 砸核桃神机已经普及了,下面就是新时代大学生小王用自己的诺基亚 1110 的把妹过程。 小王给中意的两个女生直接发短信留言(群发):

小王 -> 女 A:下午一起看个电影?[早晨 10 点] 小王 -> 女 B:下午一起看个电影?[早晨 10 点]

发完短信小王去看《西游记》去了。10分钟以后电话响起,收到了妹子的短信,小王拿起了手机阅读了消息并进行回复。

女 B -> 小王:好呀,下午 3 点来我家接我吧 [早晨 10 点 10 分] 小王 -> 女 B:好的,我去接你不见不散。

名词解释

  1. 小王发完短信不需要盯着手机看,也不需要时不时看一下手机,有短信回复会有通知,再来阅读就好了。这就是 AIO 中的 AsynchronousServerSocketChannel,可以注册一个回调 CompletionHandler,等待有消息的时候直接通知回调处理即可。

优缺点

AIO 包括了 NIO 的所有优缺点的同时,增加了异步回调的能力,由此解决的问题是不需要同步的等待非阻塞 IO 的反馈,所以 NIO 叫做异步非阻塞 IO 模型。

是时候展示真正的技术了

说了这么多,用 NIO 实现一个把妹聊天程序呗?

服务器端

代码语言:javascript
复制
public class NioServer {
    public void start() throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(6789));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务器启动成功");
        while (true) {
            selector.select();
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                if (selectionKey.isAcceptable()) {
                    iterator.remove();
                    handleAccept(serverSocketChannel, selector);
                } else if (selectionKey.isReadable()) {
                    handleRead(selectionKey);
                } else {
                    System.out.println("其他请求");
                }
            }
        }
    }

    private void handleRead(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        StringBuilder request = new StringBuilder();
        while (socketChannel.read(byteBuffer) > 0) {
            byteBuffer.flip();
            request.append(StandardCharsets.UTF_8.decode(byteBuffer));
        }
        if (request.length() > 0) {
            System.out.println("服务端收到消息:" + request.toString());
        }
    }

    private void handleAccept(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        SocketChannel socketChannel = serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("有新人进入聊天室");
        socketChannel.write(StandardCharsets.UTF_8.encode("进入聊天室,现在可以聊天了"));
    }

    public static void main(String[] args) throws IOException {
        new NioServer().start();
    }
}

客户端

代码语言:javascript
复制
public class NioClient {
    public void start() throws IOException {
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(6789));
        Selector selector = Selector.open();
        socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_READ);
        new Thread(new NioClientHandler(selector)).start();
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNextLine()) {
            String request = scanner.nextLine();
            if (request != null && request.length() > 0) {
                socketChannel.write(StandardCharsets.UTF_8.encode(request));
            }
        }
    }

    public static void main(String[] args) throws IOException {
        new NioClient().start();
    }

    private class NioClientHandler implements Runnable {
        private Selector selector;

        public NioClientHandler(Selector selector) {
            this.selector = selector;
        }

        @Override
        public void run() {
            try {
                while (true) {
                    selector.select();
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();

                    Iterator iterator = selectionKeys.iterator();
                    while (iterator.hasNext()) {
                        SelectionKey selectionKey = (SelectionKey) iterator.next();
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                        StringBuilder response = new StringBuilder();
                        while (socketChannel.read(byteBuffer) > 0) {
                            byteBuffer.flip();
                            response.append(StandardCharsets.UTF_8.decode(byteBuffer));
                        }
                        if (response.length() > 0) {
                            System.out.println("接收服务端消息:" + response);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

参考文档

  1. 图解 | 深入揭秘 epoll 是如何实现 IO 多路复用的!
  2. 解锁网络编程之NIO的前世今生
  3. NIO如何实现多路复用?
  4. 深入理解 Java IO
  5. 高并发专题之 IO 多路复用:Select、Poll、Epoll
代码语言:javascript
复制
推荐文章面试官问:前后端分离项目,有什么优缺点?我说:没
2020 年腾讯新增 20 亿行代码,鹅厂第一编程语言还是它
通俗讲解分布式锁,看完不懂算我输
写博客能月入10K?
一款基于 Spring Boot 的现代化社区(论坛/问答/社交网络/博客)更多项目源码
这或许是最美的Vue+Element开源后台管理UI推荐一款高颜值的 Spring Boot 快速开发框架
一款基于 Spring Boot 的现代化社区(论坛/问答/社交网络/博客)
13K点赞都基于 Vue+Spring 前后端分离管理系统ELAdmin,大爱想接私活时薪再翻一倍,建议根据这几个开源的SpringBoot项目
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 好好学java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • BIO:80 年代屌丝追妹
    • 名词解释
      • BIO 的缺点
      • NIO:80 年代公子哥把妹
        • 名词解释
          • 优缺点
            • 关键名词
            • AIO:21 世纪非智能时代大学生把妹
              • 名词解释
                • 优缺点
                • 是时候展示真正的技术了
                  • 服务器端
                    • 客户端
                    • 参考文档
                    相关产品与服务
                    短信
                    腾讯云短信(Short Message Service,SMS)可为广大企业级用户提供稳定可靠,安全合规的短信触达服务。用户可快速接入,调用 API / SDK 或者通过控制台即可发送,支持发送验证码、通知类短信和营销短信。国内验证短信秒级触达,99%到达率;国际/港澳台短信覆盖全球200+国家/地区,全球多服务站点,稳定可靠。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档