• 第六课:NIO简介


    一、传统BIO的缺点

    BIO属于同步阻塞行IO,在服务器的实现模型为,每一个连接都要对应一个线程。当客户端有连接请求的时候,服务器端需要启动一个新的线程与之对应处理,这个模型有很多缺陷。当客户端不做出进一步IO请求的时候,服务器端的线程就只能挂着,不能去处理其他请求。这样会对造成不必要的线程开销。

    二、阻塞与同步

    同步和异步都是由基于应用程序和操作系统处理IO事件所采用的方式所决定的。
    在这里插入图片描述
    阻塞和非阻塞式指线程在得到调用结果之前是否被挂起,主要针对线程。
    在这里插入图片描述

    三、NIO简介(同步非阻塞)

    • Java NIO全称java non-blocking IO, 是指JDK提供的新API。从JDK1.4开始,Java提供了一系列改进的输入/输出的新特性,被统称为NIO(即New IO),是同步非阻塞的。
    • NIO是一种面向缓冲区的、基于通道的IO操作,NIO有三大核心部分: Channel(通道), Buffer(缓冲区),Selector(选择器)
    • java NIO的运行模式是:客户端发送的链接请求都会被注册到Selector(选择器)上,多路复用器轮询到有I/O请求时才会启动一个线程去服务。

    四、NIO三大核心原理

    NIO有三大核心部分: Channel(通道), Buffer(缓冲区),Selector(选择器)
    Buffer(缓冲区)
    缓冲区本质上就是一块内存,数据的读写都是通过Buffer类实现的。缓冲区buffer主要是和通道数据交互,即从通道中读入数据到缓冲区,和从缓冲区中把数据写入到通道中,通过这样完成对数据的传输。
    Channel(通道)
    java NIO的类似于流,但是又有些不同:既可以从通道中读取数据,又可以写数据到通道。但流的(input和output)读写通常是单向的。通道可以非阻塞读取和写入通道,通道可以支持读取或写入缓冲区,也支持异步读写。
    Selector选择器
    Selector是一个java NIO组件,可以检测一个或多个NIO通道,并确定已经准备好进行读取或者写入。这样,一个单独的线程就可以管理多个Channel,从而管理多个网络连接,提高效率。
    在这里插入图片描述

    • 每个channel都会对应一个Buffer
    • 一个线程对应Selector,一个Selector对应多个Channel
    • 程序切换到那个channel是由事件决定
    • Selector会根据不同的事件,在各个通道上切换
    • Buffer就是一个内存块,底层就是一个数组,数据的读取和写入都是通过Buffer来实现的

    五、NIO三板斧

    在这里插入图片描述

    六、NIO实现案例

    客户端

    public class NioClient {
        public static void main(String[] args) throws IOException {
    
            SocketChannel socketChannel=SocketChannel.open();
            socketChannel.configureBlocking(false);
            InetSocketAddress address = new InetSocketAddress("127.0.0.1", 9000);
            if (!socketChannel.connect(address)) {
                while (!socketChannel.finishConnect()){
                    System.out.println("连接中,客户端可以进行其他工作");
                }
                String str="hello world!";
                ByteBuffer wrap = ByteBuffer.wrap(str.getBytes());
                socketChannel.write(wrap);
                //避免客户端中断
                System.in.read();
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    服务器端

    public class NioServer {
        public static void main(String[] args) throws IOException {
            // 获取一个ServerSocket通道
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            // serverChannel通道一直监听9000端口
            serverChannel.socket().bind(new InetSocketAddress(9000));
            // 设置serverChannel为非阻塞
            serverChannel.configureBlocking(false);
            //创建Selector选择器用来监听通道
            Selector selector = Selector.open();
            // 把ServerSocketChannel注册到selector中,并且selector对客户端的连接操作感兴趣
            SelectionKey selectionKey = serverChannel.register(selector, SelectionKey.OP_ACCEPT);
            System.out.println("服务启动成功!");
    
            while(true)
            {
                /*
                 * 如果事件没有到达 selector.select() 会一直阻塞等待
                 */
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext())
                {
                    SelectionKey key = iterator.next();
                    if (key.isAcceptable()) // 如果是OP_ACCEPT事件,则进行连接获取和事件注册
                    {
                        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("接受到客户端数据"+new String(byteBuffer.array()));
                        }else if(len==-1) //如果客户端断开连接,关闭socket
                        {
                            System.out.println("客户端断开连接!");
                            socketChannel.close();
                        }
                    }
                    // 从事件集合中删除本次处理的key,防止下次select重复处理
                    iterator.remove();
    
                }
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    【华为OD机试真题 JS】可以组成网络的服务器
    PIE-Engine APP:凉山州火灾高发地异常度分析系统
    web前端面试题附答案003-说一下你都用过那些格式的图片
    【pandas小技巧】--日期相关处理
    Flink session集群运维
    AlexNet——训练花数据集
    【智慧工地源码】智慧工地助力数字建造、智慧建造、安全建造、绿色建造
    【C++语法糖】范围for
    【深入浅出 Yarn 架构与实现】1-1 设计理念与基本架构
    系统设计.秒杀系统
  • 原文地址:https://blog.csdn.net/weixin_39038328/article/details/136386542