IO线程模型

news2024/9/25 3:26:17

文章目录

  • IO线程模型
    • 一、BIO
      • 1、概念
      • 2、Demo
        • 2.1、Demo1.0
        • 2.2、Demo2.0
        • 2.3、小结
    • 二、NIO
      • 1、概念
      • 2、Demo
        • 2.1、Demo1.0
        • 2.2、Demo2.0

IO线程模型

一、BIO

1、概念

       BIO 全称 Block-IO 是一种**同步且阻塞**的通信模式。是一个比较传统的通信方式,模式简单,使用方便。但并发处理能力低,通信耗时,依赖网速。

       同步: 可以理解为干这件事中间,不能干其他事
       阻塞: 可以理解为有事把游戏暂停了,干完事了再来继续游戏

概念不好理解,直接上Demo

2、Demo

2.1、Demo1.0

public class SocketServer {
    private static void handler(Socket clientSocket) throws Exception {
            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));
            }
    }

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8001);
        while(true){
            System.out.println("等待连接。。。");
            // 阻塞住了
            Socket clientSocket = serverSocket.accept();

            System.out.println("有客户端连接了。。。");

            handler(clientSocket);
        }
    }
}

       先理解一下这段代码里面的 Socket clientSocket = serverSocket.accept(); int read = clientSocket.getInputStream().read(bytes);,这两端代码都是阻塞的,也就是当执行到这里的时候,就会卡住了,暂时不会执行下面的东西了

启动的时候,这里控制台输出完等待连接以后,就会卡住了

在这里插入图片描述

然后这里使用一个Telnet的东西,百度一下即可
在这里插入图片描述
使用Telnet搭建一个客户端连接到上面的服务端中

在这里插入图片描述

我们这个时候再开一个Telnet客户端连接到上面的服务端

在这里插入图片描述

这时候再第一个Telnet中随便摁下键盘,你会发现控制台输出

在这里插入图片描述

我觉的这里有两点:

  • 一是当你两个Telnet连接的时候,只有第一个Telnet先显示连接,另一个Telnet没有显示,同一时间只能处理一件事(这感觉是同步
  • 二是第一个Telnet接收完之后又显示出来了第二个Telnet的连接信息,说明第二个Telnet没有被抛弃,等到第一个搞完了再轮到它(这应该就是阻塞

现在应该能体验到这种方式比较局限,它适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序直观简单易理解。

2.2、Demo2.0

稍微改进一些

public class SocketServer {

    private static void handler(Socket clientSocket) throws Exception {
            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));
            }
    }

    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8001);
        while(true){
            System.out.println("等待连接。。。");
            // 阻塞住了
            Socket clientSocket = serverSocket.accept();

            System.out.println("有客户端连接了。。。");

            new Thread(() -> {
                try {
                    handler(clientSocket);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

修改的地方也就是多开了线程去连接下一个客户端,也就是每连接一个客户端就会新开一个线程(也就是打破了阻塞),使用两个Telnet试一试
在这里插入图片描述

并且当你在两个Telnet嗯东西的时候,控制台也有反应

2.3、小结

两种方式都是BIO:

  1. 第一种就是在服务端处理完第一个客户端的所有事件之前,无法为其他服务端提供服务
  2. 第二种弥补了第一种的缺点,但是会产生大量空闲线程,徒增压力,浪费资源

这样通过通过多线程的方式,确实可以解决一些问题,但是还是会带来一些新的问题,所以要寻求更好的解决方法

二、NIO

1、概念

      Java NIO,全程 Non-Block IO ,是 Java SE 1.4 版以后,针对网络传输效能优化的新功能。是一种非阻塞同步的通信模式。

2、Demo

2.1、Demo1.0

public class NioServer {
    // 保存客户端连接
    static List<SocketChannel> channelList = new ArrayList<>();

    public static void main(String[] args) throws IOException {
        // 创建NIO ServerSocketChannel,与BIO的serverSocket类似
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(8001));

        // 设置ServerSocketChannel为非阻塞
        serverSocket.configureBlocking(false);
        System.out.println("服务启动成功");

        while (true) {
            // 非阻塞模式accept方法不会阻塞,否则会阻塞
            // NIO的非阻塞是由操作系统内部实现的,底层调用了linux内核的accept函数
            SocketChannel socketChannel = serverSocket.accept();

            if (socketChannel != null) { // 如果有客户端进行连接
                System.out.println("连接成功");
                // 设置SocketChannel为非阻塞
                socketChannel.configureBlocking(false);
                // 保存客户端连接在List中
                channelList.add(socketChannel);
            }

            // 遍历连接进行数据读取 10w - 1000 读写事件
            Iterator<SocketChannel> iterator = channelList.iterator();

            while (iterator.hasNext()) {
                SocketChannel sc = iterator.next();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                // 非阻塞模式read方法不会阻塞,否则会阻塞
                int len = sc.read(byteBuffer);
                // 如果有数据,把数据打印出来
                if (len > 0) {
                    System.out.println(Thread.currentThread().getName() + " 接收到消息:" + new String(byteBuffer.array()));
                } else if (len == -1) { // 如果客户端断开,把socket从集合中去掉
                    iterator.remove();
                    System.out.println("客户端断开连接");
                }
            }
        }
    }
}

自己看懂就可以,看不懂,看我下面

在这里插入图片描述

怎么去理解这个非阻塞是什么意思呢?你Debug启动一下项目然后,在SocketChannel socketChannel = serverSocket.accept();打一个断点,然后点下面那个按钮,你就会发现它会一直循环,这就是非阻塞了

在这里插入图片描述
然后当我们连接一个客户端的时候

在这里插入图片描述
在这里插入图片描述

这里就可以看出来,accept后,通过不断的轮询channelist中的连接,有则打印出来,没有就继续accept,没有中间阻塞的情况,这里也没有使用多线程,也就是说用一个线程,完成了BIO那里开多个线程完成的事情

这里可以同时开几个Telnet,然后Debug启动服务端,进行一下联调,差不多就能理解点了

这里会发现还有优化的空间,如果我们这里连接了10w个客户端,但是只有1w个客户端有真正的事件发生,我们的关注点应该在那1w个上面,如果我们每次都要去遍历这10w个客户端的话,很头疼的

2.2、Demo2.0

这个就是解决了上面的那个问题,使用了多路复用器

public class NioSelectorServer {
    public static void main(String[] args) throws IOException {
        int OP_ACCEPT = 1 << 4;
        System.out.println(OP_ACCEPT);

        // 创建NIO ServerSocketChannel
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(8001));

        // 设置ServerSocketChannel为非阻塞
        serverSocket.configureBlocking(false);

        // 打开Selector处理Channel,即创建epoll
        Selector selector = Selector.open();

        // 把ServerSocketChannel注册到selector上,并且selector对客户端accept连接操作感兴趣
        SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务启动成功");

        while (true) {
            // 阻塞等待需要处理的事件发生 已注册事件发生后,会执行后面逻辑
            selector.select();

            // 获取selector中注册的全部事件的 SelectionKey 实例
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

            // 遍历SelectionKey对事件进行处理
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // 如果是OP_ACCEPT事件,则进行连接获取和事件注册
                if (key.isAcceptable()) {
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = server.accept();
                    socketChannel.configureBlocking(false);
                    // 这里只注册了读事件,如果需要给客户端发送数据可以注册写事件
                    SelectionKey selKey = socketChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("客户端连接成功");
                } else if (key.isReadable()) {  // 如果是OP_READ事件,则进行读取和打印
                    SocketChannel socketChannel = (SocketChannel) key.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    int len = socketChannel.read(byteBuffer);
                    // 如果有数据,把数据打印出来
                    if (len > 0) {
                        System.out.println(Thread.currentThread().getName() +  "接收到消息:" + new String(byteBuffer.array()));
                    } else if (len == -1) { // 如果客户端断开连接,关闭Socket
                        System.out.println("客户端断开连接");
                        socketChannel.close();
                    }
                }
                //从事件集合里删除本次处理的key,防止下次select重复处理
                iterator.remove();
            }
        }
    }
}

这个为了解决上面那个问题,加入了多路复用,为不让他做一些无用的循环遍历,抛弃了channellist集合,把连接都注册到多路复用器里面

在这里插入图片描述

我大致理解的就是这样子的,可能不太周到

这样子处理的话,如果连接了10w个连接,当有事件的连接过来的时候,就会去处理该连接,而不会全局的循环,也就避免了时间上的消耗

还有一个AIO,之后遇到了再总结吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/430689.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

万字长文的BI百科全解

目前来看&#xff0c;现今世界未来的发展方向基本已经确定&#xff0c;数字化的趋势已经化身为一股不可阻挡的浪潮&#xff0c;各国也都宣布了数字化、数据、数字经济、数字化转型等方面的相关发展政策法规&#xff0c;明确未来的战略方针。同时世界传统经济增长也开始乏力&…

6 计时器(一)

计时器 6.1 TIM TIM简介 TIM&#xff08;Timer&#xff09;定时器 定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中…

2023 年 3 月 NFT 月度报告

作者&#xff1a;Danielfootprint.network 数据来源&#xff1a;NFT Monthly Report 三月份的 NFT 市场上出现了两个有趣的趋势。一方面&#xff0c;Polygon 链尽管在二月份有所突破&#xff0c;达到了 NFT 总交易量的 4.2%&#xff0c;但于三月再次跌至 1% 以下&#xff0c;…

55 openEuler搭建Mariadb数据库服务器-配置环境

文章目录 55 openEuler搭建Mariadb数据库服务器-配置环境55.1 关闭防火墙并取消开机自启动55.2 修改SELINUX为disabled55.3 创建组和用户55.4 创建数据盘55.4.1 方法一&#xff1a;在root权限下使用fdisk进行磁盘管理55.4.2 方法二&#xff1a;在root权限下使用LVM进行磁盘管理…

【C语言】基础语法1:变量和数据类型

❤️‍&#x1f525;前情提要❤️‍&#x1f525;   欢迎来到C语言基本语法教程   在本专栏结束后会将所有内容整理成思维导图&#xff08;结束换链接&#xff09;并免费提供给大家学习&#xff0c;希望大家纠错指正。本专栏将以基础出发&#xff0c;在之后的教程中将会不断…

MySQL调优笔记——慢SQL优化记录(2)

今天调优的原因是&#xff0c;有一个统计报表业务&#xff0c;查询的时间太慢&#xff1b;同时由于数据库的压力是随机性的&#xff0c;这个业务的执行下限和上限相差近20倍&#xff1b;快的时候可以达到600ms&#xff0c;慢的时候有9秒之多&#xff1b; 接下来详细介绍&#x…

STL--string

一、string介绍 string是表示字符序列的对象。 标准字符串类通过类似于标准字节容器的接口为此类对象提供支持&#xff0c;但添加了专门设计用于处理单字节字符字符串的功能。 字符串类是 basic_string 类模板的实例化&#xff0c;该模板使用char作为其字符类型&#xff0c;以…

【云原生进阶之容器】第五章容器运行时5.7--容器逃逸原理

《云原生进阶之容器》专题索引: 第一章Docker核心技术1.1节——Docker综述第一章Docker核心技术1.2节——Linux容器LXC第一章Docker核心技术1.3节——命名空间Namespace第一章Docker核心技术1.4节——chroot技术第一章Docker核心技术1.5.1节——cgroup综述

为什么要对员工行为进行安全管控?

ChatGPT的火热已不是一件新鲜事了。而现在更让大家热议的是&#xff0c;ChatGPT带来的网络安全风险&#xff0c;不少安全人员对此担心不已。因ChatGPT引起的网络安全事件&#xff0c;媒体也多有报道。 例如&#xff0c;近日&#xff0c;外媒就报道了三星因ChatGPT泄露了机密信息…

ChatGPT - 基于 Visual Studio Code 进行 AI 编码

2023-04-15 周六 杭州 晴 前情提要 ChatGPT &#xff0c;AutoGPT , AgentGPT 现在各种人工智能编码工具层出不穷&#xff0c;通过人工智能编码再也不用记大量的技术知识点了&#xff0c;现在所谓开发人员的经验性的东西也好像弱化了很多&#xff0c;我们可以更有效率的做自己…

Qt/QML编程学习之心得:QML界面设计(二)

都知道Qt是一种界面设计语言,那么它有几种设计方式UI technologies呢?答案是5种。 1、传统桌面所见即所得Classic desktop look-and-feel• Qt Widgets 2、 流畅、现代+触控Fluent, modern touch-based UX• Qt Quick 3、 三维3D becomes more and more important• Qt Open…

安全运营之资产安全信息管理

安全风险管理的三要素分别是资产、威胁和脆弱性&#xff0c;脆弱性的存在将会导致风险&#xff0c;而威胁主体利用脆弱性产生风险。网络攻击主要利用了系统的脆弱性。由于网络管理对象&#xff08;资产&#xff09;自身的脆弱性&#xff0c;使得威胁的发生成为可能&#xff0c;…

Linux 函数调用过程解析

一、Linux 函数调用过程解析 在x86的计算机系统中&#xff0c;内存空间中的栈主要用于保存函数的参数&#xff0c;返回值&#xff0c;返回地址&#xff0c;本地变量等。一切的函数调用都要将不同的数据、地址压入或者弹出栈。因此&#xff0c;为了更好地理解函数的调用&#xf…

QT 常见面试题

1、在Qt当中&#xff0c;多线程环境下&#xff0c;信号槽分别是在什么样的线程中执行&#xff1f;如何控制&#xff1f; 参考答案&#xff1a; 通过connect(...)第5个参数控制信号槽执行所在线程 connect(...)它的连接方式&#xff1a;队列连接、自动连接和直接连接 队列连…

EMC-MLCC电容反谐振点引起的RE辐射超标

MLCC电容反谐振点引起的RE辐射超标 对待RE辐射问题&#xff0c;可以按照干扰源&#xff0c;干扰路径&#xff0c;被干扰源&#xff0c;入手较多的是干扰源和干扰路径&#xff0c; 解决干扰源可以从展频&#xff0c;调频&#xff0c;屏蔽干扰源&#xff0c;增加RC snabber吸收…

ChatGPT颠覆者来了,能替代90%的人的工作?

前言 Auto GPT是一个实验性开源应用程序&#xff0c;展示了GPT-4语言模型的功能。该程序由GPT-4驱动&#xff0c;将LLM“思想”链接在一起&#xff0c;以自主实现您设定的任何目标。作为GPT-4完全自主运行的首批例子之一&#xff0c;Auto GPT突破了人工智能的极限。 特征 &a…

明面抵制,暗中布局 对于AI,马斯克的言行为何如此“割裂”?

最近&#xff0c;马斯克创建了一家叫做“X”的空壳公司&#xff0c;目标是将其打造成涵盖各方面的多功能应用集合平台&#xff0c;推特、SpaceX、特斯拉、Neuralink等公司业务都已打包加入其中。如今&#xff0c;“X”公司再添新丁——X.AI&#xff0c;即马斯克新成立的人工智能…

【硬件外设使用】——PWM

【硬件外设使用】——PWMPWM基本概念PWM使用方法pyb.pwm方法属性machine.pwm方法属性PWM可用的传感器PWM基本概念 PWM是脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;的缩写&#xff0c;是一种用数字信号控制模拟电路输出的技术。PWM通常被用来控制电压或电流的…

【 Spring MVC 核心功能(三) - 输出数据】

文章目录引言一、返回静态页面二、返回非静态页面的数据三、返回 JSON 对象四、请求转发(forward)和请求重定向(redirect)五、拓展&#xff1a;IDEA 热部署(热加载)3.1 添加 SpringBoot DevTools 框架3.2 开起 IDEA 的自动编译3.3 开起运行中的热部署3.4 使用 debug 启动项目引…

Docker系列 基于OpenAI API自建ChatGPT

转自我的博客文章https://blognas.hwb0307.com/linux/docker/4201&#xff0c;内容更新仅在个人博客可见。欢迎关注&#xff01; 前言 我用帐号/密码使用chatGPT已经有一段时间。但是&#xff0c;我有几个私交较密的朋友&#xff0c;他们并不具备使用chatGPT的条件&#xff1b…