62.NIO多路io复用

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

1.阻塞式 与 非阻塞式

在这里插入图片描述
类似 悲观锁与乐观锁

2. 客户端 从键盘重复输入

while (true){
String write = new Scanner(System.in).next();
socket.getOutputStream().write(write.getBytes());
}

public class ClientSocket {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket();
            socket.connect(new InetSocketAddress("127.0.0.1",8080));
            while (true){
                String write = new Scanner(System.in).next();
                socket.getOutputStream().write(write.getBytes());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 消耗资源

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

4.引入nio操作

  1. java 1.4推出io 对原来bio(阻塞式)进行优化
  2. nio 英文: no blocking io 非阻塞
  3. 核心:面向缓存区 基于通道实现非阻塞式io 多路io复用实现(靠选择器实现)

5.bio与nio区别:

bio: 阻塞式io 面向流传输 根据字节传输 效率很低
nio: 阻塞式io 面向缓存区 最大亮点:io多路复用机制

6. io多路复用

在这里插入图片描述

public class NioServerTcp {
    static ByteBuffer byteBuffer = ByteBuffer.allocate(512);

    // 存放多个不同的tcp连接
    private static List<SocketChannel> socketChannels = new ArrayList<>();

    //多路(多个不同的tcp连接),io复用:只要一个线程去维护多个不同的io操作 最大的好处是:保证线程安全问题、减少cpu调度资源
    //选择的io模型 非阻塞式
    public static void main(String[] args) {

        try {
            // 创建ServerSocketChannel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            //  绑定端口号码
            serverSocketChannel.bind(new InetSocketAddress(8080));
            //设置为非阻塞式
            serverSocketChannel.configureBlocking(false);
            while (true) {
                // 监听我们的消息  socketChannel为空没有人给我消息
                SocketChannel socketChannel = serverSocketChannel.accept();
                if (socketChannel != null) {
                    // 已经有人帮我发送消息
                    socketChannel.configureBlocking(false);
                    socketChannels.add(socketChannel);
                }
                // 直接遍历我们的选择器所有的SocketChannel
                for (SocketChannel scl : socketChannels) {
                    // 遍历读取
                    int j = scl.read(byteBuffer);
                    if (j > 0) {
                        byteBuffer.flip();
                        byte[] bytes = Arrays.copyOf(byteBuffer.array(), byteBuffer.limit());
                        System.out.println("获取到数据" + new String(bytes));
                    }
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
    // 为什么for在while里面
}

7.NIO的核心组件

1.通道(Channel)

通常我们nio所有的操作都是通过通道开始的,所有的通道都会注册到统一个选择器(Selector)上实现管理,在通过选择器将数据统一写入到 buffer中。

2.缓冲区(Buffer)

Buffer本质上就是一块内存区,可以用来读取数据,也就先将数据写入到缓冲区中、在统一的写入到硬盘上。

3.选择器(Selector)

Selector可以称做为选择器,也可以把它叫做多路复用器,可以在单线程的情况下可以去维护多个Channel,也可以去维护多个连接;
在这里插入图片描述
在这里插入图片描述

public class NIOServer {

    /**
     * 创建一个选择器
     */
    private Selector selector;

    public void initServer(int port) throws IOException {
        // 获得一个ServerSocketChannel通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 设置通道为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 将该通道对应的ServerSocket绑定到port端口     
        serverSocketChannel.bind(new InetSocketAddress(port));
        // 获得一个通道管理器
        this.selector = Selector.open();
        // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后,
        // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void listen() throws IOException {
        System.out.println("服务端启动成功!");
        // 轮询访问selector
        while (true) {
            // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞
            int select = selector.select(10);
            if (select == 0) {
                continue;
            }
            // 消息类型有很多种 、发送消息连接

            // 获得selector中选中的项的迭代器,选中的项为注册的事件
            Iterator<SelectionKey> ite = this.selector.selectedKeys().iterator();
            while (ite.hasNext()) {
                SelectionKey key = (SelectionKey) ite.next();
                // 删除已选的key,以防重复处理
                ite.remove();

                // 发出一个消息 蚂蚁课堂牛逼
                if (key.isAcceptable()) {// 客户端请求连接事件
                    ServerSocketChannel server = (ServerSocketChannel) key.channel();
                    // 获得和客户端连接的通道
                    SocketChannel channel = server.accept();
                    // 设置成非阻塞
                    channel.configureBlocking(false);

                    // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。
                    channel.register(this.selector, SelectionKey.OP_READ);

                } else if (key.isReadable()) {// 获得了可读的事件
                    read(key);
                }

            }

        }
    }

    public void read(SelectionKey key) throws IOException {
        // 服务器可读取消息:得到事件发生的Socket通道
        SocketChannel channel = (SocketChannel) key.channel();
        // 创建读取的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(512);
        channel.read(buffer);
        byte[] data = buffer.array();
        String msg = new String(data).trim();
        System.out.println("服务端收到信息:" + msg);
        ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes("utf-8"));
        channel.write(outBuffer);// 将消息回送给客户端
    }

    public static void main(String[] args) throws IOException {
        NIOServer server = new NIOServer();
        server.initServer(8080);
        server.listen();
    }

8.Redis是如何单线程能够非常好支持并发

Redis官方没有windows版本redis,只有linux版本的reids

Redis的底层是采用nio 多路io复用机制实现对多个不同的连接(tcp)实现io的复用;能够非常好的支持高并发,同时能够先天性支持线程安全的问题。

io的复用::使用一个线程维护多个不同的io操作 原理使用nio的选择器,将多个不同的Channel统一交给我们的selector(选择器管理)

但是nio的实现在不同的操作系统上存在差别:在我们windows操作系统上使用select实现轮训机制、在linux操作系统使用epoll
备注:windows操作系统是没有epoll
在windows操作系统中使用select实现轮训机制时间复杂度是为 o(n),而且这种情况也会存在空轮训的情况,效率非常低、其次默认对我们的轮训有一定限制,所以这样的话很难支持上万tcp连接。

所以在这时候linux操作就出现epoll实现事件驱动回调形式通知,不会存在空轮训的情况,对活跃的socket实现主动回调,这样的性能有很大的提升 所以时间复杂度为是o(1)

注意:windows操作系统没有epoll、只有linux操作系统有。

所以为什么Nginx、redis能够支持非常高的并发 最终都是靠的linux版本的 io多路复用机制epoll

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值