BIO、NIO线程模型

这篇具有很好参考价值的文章主要介绍了BIO、NIO线程模型。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

BIO(Blocking IO)

BIO(Blocking IO):同步阻塞模型,一个客户端连接对应一个处理线程,即:一个线程处理一个客户端请求。
    单线程版本: 服务端在处理完第一个客户端的所有事件之前,无法为其他客户端提供服务。
    多线程版本:如果出现大量只连接不发数据的话,那么就会一直占用线程,浪费服务器资源。
    线程池版本:用线程池根本不能根本的解决问题。假如我们有500个线程组成一个线程池,我们用这500个线程去为客户端服务。那么,假设前500个客户端请求进来,被占满了这500个线程,并且这500个客户端连接只连接不发数据,那么我们的服务端不就崩掉了吗?因为我们服务端最多只能处理500个客户端连接,而你后面进来的,不管多少都会被我们的服务端拒之门外,所以用线程池也不能解决这个问题。

public class SocketServer {
    public static void main(String[] args) throws Exception {
        //创建了服务端,绑定到了9001端口
        ServerSocket serverSocket = new ServerSocket(9001);
        while (true) {
            System.out.println("等待连接..");
            //阻塞方法
            Socket clientSocket = serverSocket.accept(); //监听客户端的连接,有客户端的连接代码才会往下走,没有连接就会阻塞在这里
            System.out.println("有客户端连接了..");

                        //1.单线程版本
            //handler(clientSocket);

            //2.多线程版本
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(clientSocket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            //3.线程池版本
            ExecutorService executorService = Executors.newFixedThreadPool(100);
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        handler(clientSocket);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    //handler方法是处理客户端事件的方法,比如客户端发来数据,在这里我们就做打印处理
    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));
            }
    }
}

NIO(Non Blocking IO)

NIO模型1.0版本(早期版本)

一个线程无限循环去list(存放着客户端连接)轮训,检查是否有读写请求,如果有则处理,如果没有跳过。这个版本如果连接数特别多的话,会有大量的无效遍历,假如有1000个客户端连接,只有其中100个有读写事件,那么还是会循环1000次,其中的900次循环都是无效的。

public class NioServer {

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

    public static void main(String[] args) throws IOException {

        // 创建NIO ServerSocketChannel,与BIO的serverSocket类似,即创建了服务端并绑定了9001端口
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(9001));
        // 设置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);
            }
            // 遍历连接进行数据读取  读写事件
            Iterator<SocketChannel> iterator = channelList.iterator();
            while (iterator.hasNext()) {
                SocketChannel sc = iterator.next();
                ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                // 非阻塞模式read方法不会阻塞,否则会阻塞
                int len = sc.read(byteBuffer);
                // 如果有数据,把数据打印出来,整个过程是一个main线程在处理
                if (len > 0) {
                    System.out.println(Thread.currentThread().getName() + " 接收到消息:" + new String(byteBuffer.array()));
                } else if (len == -1) { // 如果客户端断开,把socket从集合中去掉
                    iterator.remove();
                    System.out.println("客户端断开连接");
                }
            }
        }
    }
}

NIO模型2.0版本(启用多路复用器,jdk1.4以上)

客户端发送的连接请求都会注册到多路复用器selector上,多路复用器轮询到连接有IO请求就进行处理,JDK1.4开始引入。这个版本进行了很大程度的优化,当客户端连接以后,如果有读写事件,则会加入一个队列里,处理事件的线程会阻塞等待这个队列里有新的元素之后处理事件。

步骤:

  1. 创建ServerSocketChannel服务端
  2. 创建多路复用器Selector( 每个操作系统创建出来的是不一样的,Centos创建的是EPollSelectorProviderImpl,Windows创建的是WindowsSelectorImpl,其实就是Linux的Epoll实例EPollArrayWrapper)
  3. ServerSocketChannel将建立连接事件注册到Selector中(register方法往EPollArrayWrapper中添加元素)
  4. 处理事件
    1. 如果是建立连接事件,则把客户端的读写请求也注册到Selector中
    2. 如果是读写事件则按业务处理
public class NioSelectorServer {

    public static void main(String[] args) throws IOException {
        //指定感兴趣的操作类型为 OP_ACCEPT
        int OP_ACCEPT = 1 << 4;
        System.out.println(OP_ACCEPT);

        // 创建NIO ServerSocketChannel
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        serverSocket.socket().bind(new InetSocketAddress(9001));
        // 设置ServerSocketChannel为非阻塞
        serverSocket.configureBlocking(false);
        // 打开Selector处理Channel,即创建epoll,这里启用了多路复用器是与NIO1.0版本最大的区别
        Selector selector = Selector.open();
        // 把ServerSocketChannel注册到selector上,并且selector对客户端accept连接操作感兴趣(把连接事件注册到多路复用器里面)
        SelectionKey selectionKey = serverSocket.register(selector, SelectionKey.OP_ACCEPT);//通过 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();
            }
        }
    }
}

Redis线程模型

Redis就是典型的基于epoll的NIO线程模型(nginx也是),epoll实例收集所有事件(连接与读写事件),由一个服务端线程连续处理所有事件命令。文章来源地址https://www.toymoban.com/news/detail-492721.html

到了这里,关于BIO、NIO线程模型的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Linux学习】多线程——同步 | 条件变量 | 基于阻塞队列的生产者消费者模型

    🐱作者:一只大喵咪1201 🐱专栏:《Linux学习》 🔥格言: 你只管努力,剩下的交给时间! 以生活中消费者生产者为例: 生活中,我们大部分人都扮演着消费者的角色,会经常在超市买东西,比如买方便面,而超市的方便面是由供应商生成的。所以我们就是消费者,供应商

    2024年02月05日
    浏览(51)
  • BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程

    上文介绍了网络编程的基础知识,并基于 Java 编写了 BIO 的网络编程。我们知道 BIO 模型是存在巨大问题的,比如 C10K 问题,其本质就是因其阻塞原因,导致如果想要承受更多的请求就必须有足够多的线程,但是足够多的线程会带来内存占用问题、CPU上下文切换带来的性能问题

    2024年02月14日
    浏览(48)
  • Java中的三种I/O模型:BIO、NIO和AIO

    I/O(输入/输出)操作是任何应用程序中必不可少的一部分,它涉及到与文件、网络或其他设备之间的数据传输。Java提供了几种不同的I/O模型,其中最常见的是AIO(异步非阻塞I/O)、BIO(阻塞I/O)和NIO(非阻塞I/O)。这些模型在处理I/O操作时具有不同的工作方式、特性和适用

    2024年02月08日
    浏览(38)
  • 【Netty专题】【网络编程】从OSI、TCP/IP网络模型开始到BIO、NIO(Netty前置知识)

    我是有点怕网络编程的,总有点【谈网色变】的感觉。为了让自己不再【谈网色变】,所以我想过系统学习一下,然后再做个笔记这样,加深一下理解。但是真要系统学习,其实还是要花费不少时间的,所以这里也只是简单的,尽可能地覆盖一下,梳理一些我认为比较迫切需

    2024年02月06日
    浏览(62)
  • 33. bio和nio

    1.1 bio网络模型示意图 单个客户端向服务器发起请求时,请求顺序如下: 多个客户端向一个服务器发起请求时,请求顺序如下: 1.2 bio网络模型缺点 1.阻塞式I/O 2.弹性伸缩能力差 3.多线程耗费资源 2.1 nio网络模型示意图 单个客户端向服务器发起请求时,请求顺序如下: 2.2 ni

    2024年02月17日
    浏览(40)
  • NIO与BIO

    当谈到 Java 网络编程时,经常会听到两个重要的概念:BIO(Blocking I/O,阻塞 I/O)和 NIO(Non-blocking I/O,非阻塞 I/O)。它们都是 Java 中用于处理 I/O 操作的不同编程模型。 BIO 是 Java 最早的 I/O 模型,也是最简单的一种。在 BIO 模型中,每个 I/O 操作都会阻塞当前线程,直到数据

    2024年04月10日
    浏览(45)
  • BIO、NIO和AIO

    目录 一.引言 何为IO IO的过程 Java的3种网络IO模型 阻塞和非阻塞IO IO多路复用 异步和同步IO 二.BIO 三.NIO 1. 三大组件 Channel Buffer Selector 2.ByteBuffer 2.1ByteBuffer的使用 2.2ByteBuffer 结构 ​2.3ByteBuffer的常用方法 分配空间   向 buffer 写入数据 从 buffer 读取数据 字符串与 ByteBuffer 互转 分

    2024年02月12日
    浏览(38)
  • 【java】BIO、NIO、AIO

    同步阻塞IO,使用BIO读取数据时,线程会阻塞住,并且需要线程主动去查询是否有数据可读,并且需要处理完一个Socket之后才能处理下一个Socket 在这种模型下,每个 I/O 操作都会阻塞当前线程,直到操作完成才会返回。这意味着当一个线程执行 I/O 操作时,它会被阻塞,无法执

    2024年04月10日
    浏览(36)
  • BIO、NIO、AIO 的区别

    Java面试题  阻塞IO。一个连接一个线程,当服务端接受到多个客户端的请求时,客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销 同步非阻塞IO 。一个线程处理多个连接。NIO 包含  Channel(通道)、Selector(选择

    2024年01月20日
    浏览(34)
  • BIO、NIO、AIO区别详解

    主线程发起io请求后,需要等待当前io操作完成,才能继续执行。 引入selector、channel、等概念,当主线程发起io请求后,轮询的查看系统是否准备好执行io操作,没有准备好则主线程不会阻塞会继续执行,准备好主线程会阻塞等待io操作完成。 主线程发起io请求后,不会阻塞,

    2024年02月07日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包