NIO的浅了解

news2024/12/23 13:06:12

一、五种IO类型

1、阻塞IO

用户进程一直等待数据准备好,在复制完成之前都是阻塞的

2、非阻塞IO

用户进程需要不断轮询查看是否数据准备好

优化了提升并发连接数量,但是每一个请求都需要创建一个socket建立连接,每个线程都需要去遍历轮询,导致cpu的消耗,直到数据返回成功

3、IO复用

(1)select/poll

单个线程可以处理多个网络连接。相对于BIO多了一步,注册到selector的过程,进程被selector函数阻塞。

select单个进程所能打开的fd()【就是文件描述符,linux内把所有的外部设备都看成一个文件操作】,对于每一个文件的读写操作都会调用内核提供的系统命令来返回给fd,对于socket的读写也会返回一个fd,所以放fd的时候,说明数据是可读或者是可写。

所以这个模型在单进程里,操作的连接数默认是1024,可以修改,但是会带来网络性能的下降,因为会去加大轮询次数,带来网络延迟,因为只有少数连接处于活跃状态,而每次轮询是查询所有的连接。

jdk1.6之前是使用这种模型

(2)epoll

jdk1.6后使用此模型。解决select-poll的缺陷

  • 对单个进程锁打开的连接数没有限制(连接需要占用内存,1TB大概10w个连接)
  • 利用每个fd上的callback函数来实现异步回调,省去了轮询的开销
  • mmap:通过内核和用户空间映射同一块内存空间,来减少内存复制

4、异步IO

二、NIO

1、了解

基于通道和缓冲区操作的:

  • 通道:一个新的原始IO
  • 缓冲支持:负责数据存储和传输的缓冲区
  • 具体:数据从通道读取到缓冲,数据从缓冲区写入通道

非阻塞的:

  • 针对网络的,当线程从通道读取数据到缓冲区,线程依然可以进行其他事情(1.6以后epoll模型)

选择器:

  • 用于监听多个通道的事件,例如链接打开、数据到达
  • 单个线程可以监听多个数据通道

2、Channel

FileChannel:从文件中读写数据(不适合Selector,因为不能非阻塞)

DatagramChannel:通过UDP协议读写网络中的数据

SocketChannel:通过TCP协议读写网络中的数据

ServerSocketChannel:监听一个TCP连接,对于每一个新的客户端连接都会创建一个SocketChannel。

3、Buffer

buffer是一个对象,包含需要写入或者刚读出的数据,常用的缓冲区类型是ByteBuffer

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            FileInputStream fis = new FileInputStream(new File("D:/test.txt"));
            FileOutputStream fos = new FileOutputStream(new File("D:/test.txt"));
            FileChannel fin = fis.getChannel();
            FileChannel fout = fos.getChannel();

            //初始化一个缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            //读取数据到缓冲区
            fin.read(buffer);
            //从读转化为写
            buffer.flip();
            fout.write(buffer);
            //重置缓冲区
            buffer.clear();

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

4、IO和NIO的区别

类型操作区域处理数据IO
IO面向最初的数据源

每次读取时=读取所有字节或字符,无缓存

无法前后移动读取流中的数据

当一个线程在读或者写的时候,当数据被完全读取/写入之后,并且数据未准备好时,线程不能做其他任务,只能一直等待。

当线程处于活跃状态并且外部未准备好时,阻塞。

NIO面相缓冲区

先将数据读取到缓冲区

可在缓冲区前后移动数据流

当一个线程向某个通道发送请求时,当数据被完全读取/写入,并且数据未准备好时,线程可以操作其他任务,直到数据准备后再切换回原通道,继续读写,也就是selector的使用。

外部准备好时才唤醒线程,则不会阻塞。

5、缓冲区的内部细节

缓冲区本质是一块可以写入数据,以及从中读取数据的内存,实际上也是一个byte[]数据,只是在NIO中被封装成了NIO Buffer对象,并提供了一组方法来访问这个内存块。

- capacity:容量

- position:位置。读是跟踪从缓冲区读取了多少数据;写是数据放入数组哪个位置;

- limit:写是还有多少数据取出来写到通道;读是还有多少空间可以放出去;

例如:写入

初始话时,capacity是8,就是一个8长度的格子,当写入“hello”的时候,就是ficn.reds("hello);时,postion和limit都会指向4(从0开始),当flip()时,position指向0,limit是4,0-4就是要写入的字符,最后write()写入。

6、零拷贝的原理

IO流程:内核给磁盘发送命令需要读取磁盘的数据,在DMA的控制下,把磁盘上的数据读入到内核缓冲区,内核把数据从内核缓冲区复制到用户缓冲区。用户缓冲区再将数据拷贝到Socket buffer(也是内核),最后发送到网卡缓冲区,四个步骤。

设计到一个用户态到内核态的切换,影响cpu的性能。

从内核态到用户缓冲区没有用,为什么要设计呢

为了提升IO性能,假设应用程序进行读,内核缓冲区对于读相当于一个缓冲空间,当用户只读取一小部分数据的时候,但是内核从磁盘会读取一块数据,下次用户再读其他的数据的时候,在内核缓冲区已经存在就不需要再去磁盘获取,从这个角度是提升了性能的。

使用内存映射缓存mmap,通过内核和用户空间映射同一块内存空间,来减少内存复制。

零拷贝就是减少拷贝次数,减少内核态和用户态之间的数据的复制,提升IO性能。

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            FileChannel in = FileChannel.open(Paths.get("D:/logo.png"), StandardOpenOption.READ);
            FileChannel out = FileChannel.open(Paths.get("D:/logo_cp.png"), StandardOpenOption.READ, StandardOpenOption.CREATE, StandardOpenOption.WRITE);

            MappedByteBuffer inMap = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
            MappedByteBuffer outMap = in.map(FileChannel.MapMode.READ_WRITE, 0, in.size());

            byte[] bytes = new byte[inMap.limit()];
            inMap.get(bytes);
            outMap.put(bytes);

            in.close();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

public class ccc {
    public static void main(String[] args) {
        try {
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress("localhost", 9090));
            FileChannel channel = new FileInputStream("D:/1.txt").getChannel();
            int position = 0;
            long size = channel.size();
            while (size > 0) {
                long tf = channel.transferTo(position, size, socketChannel);
                if (tf > 0) {
                    position += tf;
                    size = tf;
                }
            }
            socketChannel.close();
            channel.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public class ddd {
    public static void main(String[] args) {
        try {
            ServerSocketChannel channel = ServerSocketChannel.open();
            channel.socket().bind(new InetSocketAddress(9090));
            SocketChannel accept = channel.accept();
            ByteBuffer allocate = ByteBuffer.allocate(1024);
            int r = 0;
            FileChannel fileChannel = new FileOutputStream("D:/text_cp.txt").getChannel();
            while (r != -1){
                r = accept.read(allocate);
                allocate.flip();
                fileChannel.write(allocate);
                allocate.clear();
            }

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

    }
}

7、SocketChannel和ServerSocketChannel

服务端

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            //支持两种模式:阻塞、非阻塞
            ServerSocketChannel open = ServerSocketChannel.open();
            open.configureBlocking(false);
            //绑定端口号
            open.socket().bind(new InetSocketAddress(9090));
            while (true) {
                SocketChannel accept = open.accept();
                //存在连接
                if (accept != null) {
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    accept.read(buffer);
                    System.out.println(new String(buffer.array()));
                    //再把消息写回到客户端
                    Thread.sleep(10000);
                    buffer.filp();
                    accept.write(buffer);
                } else {
                    Thread.sleep(1000);
                    System.out.println("无客户端连接");
                }
            }
        } catch (FileNotFoundException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

客户端

public class aaa {
    public static void main(String[] args) throws IOException {
        try {
            SocketChannel open = SocketChannel.open();
            //把客户端设置为非阻塞,在非阻塞模式下,不一定是等到连接建立之后再往下执行
            open.configureBlocking(false);
            open.connect(new InetSocketAddress("localhost", 9090));
            if(open.isConnectionPending()) {
                open.finishConnect();
            }
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            buffer.flip();
            open.write(buffer);
            //读取服务端返回的数据
            buffer.clear();
            //非阻塞模式,这里不阻塞
            int r = open.read(buffer);
            if(r > 0) {
                System.out.println("收到服务端的消息" + new String(buffer.array()));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}

8、选择器Selector

一个单独的线程可以管理多个channel,从而管理多个网络连接。

public class aaa {

    static Selector selector;

    public static void main(String[] args) throws IOException {
        //创建一个多路复用器
        Selector selector = Selector.open();
        ServerSocketChannel channel = ServerSocketChannel.open();
        //连接的非阻塞
        channel.configureBlocking(false);
        channel.socket().bind(new InetSocketAddress(9090));
        //监听连接事件,会返回一个SelectionKey,通道的唯一标识
        channel.register(selector, SelectionKey.OP_ACCEPT);
        //轮询
        while (true) {
            //阻塞,所有注册到复用器上事件
            selector.select();
            //一旦某个channel准备就绪,就返回他们的key
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //一定是已经就绪的通道
                SelectionKey next = iterator.next();
                //拿到通道后,可以处理了,避免重复处理
                iterator.remove();
                if(next.isAcceptable()){
                    //连接事件
                    HandleAccept(next);
                }else if(next.isReadable()){
                    //读事件
                    HandleRead(next);
                }
            }
        }

    }

    private static void HandleAccept(SelectionKey selectionKey) throws IOException {
        ServerSocketChannel serverSocketChannel = (ServerSocketChannel)selectionKey.channel();
        SocketChannel accept = serverSocketChannel.accept();
        //IO的非阻塞
        accept.configureBlocking(false);
        accept.write(ByteBuffer.wrap("Server write".getBytes()));
        //注册的是accept的读事件
        accept.register(selector, SelectionKey.OP_ACCEPT);
    }

    private static void HandleRead(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel =  (SocketChannel)selectionKey.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        socketChannel.read(buffer);
        System.out.println("Server receive msg: " + new String(buffer.array()));

    }
}
public class bbb {
    static Selector selector;

    public static void main(String[] args) {
        try {
            selector = Selector.open();
            SocketChannel socketChannel = SocketChannel.open();
            socketChannel.configureBlocking(false);
            socketChannel.connect(new InetSocketAddress("localhost", 9090));
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            while (true) {
                selector.select();
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    SelectionKey next = iterator.next();
                    iterator.remove();
                    if(next.isConnectable()){
                        //连接事件
                        HandleConnect(next);
                    }else if(next.isReadable()){
                        //读事件
                        HandleRead(next);
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void HandleConnect(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel =  (SocketChannel)selectionKey.channel();
        if(socketChannel.isConnectionPending()){
            socketChannel.finishConnect();
        }
        socketChannel.configureBlocking(false);
        socketChannel.write(ByteBuffer.wrap("Client receive".getBytes()));
        socketChannel.register(selector, SelectionKey.OP_READ);
    }

    //读取服务端返回的数据
    private static void HandleRead(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel =  (SocketChannel)selectionKey.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        socketChannel.read(buffer);
        System.out.println("Client receive msg: " + new String(buffer.array()));
    }
}

9、BIO\NIO\AIO的区别

BIONIOAIO
阻塞阻塞:一个线程执行IO操作会被阻塞非阻塞:线程可以同时处理多个IO请求非阻塞
同步同步:需要等待IO操作完成后才能继续执行异步:轮询方式=channel+buffer+selector回调机制
处理    面向流缓冲区面向事件
并发低,需要创建大量线程通过单线程或少量线程处理同左
场景连接数少且吞吐量不高连接数较多但请求量较小高并发

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

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

相关文章

ZooKeeper+Kafka+ELK+Filebeat集群搭建实现大批量日志收集和展示

大致流程&#xff1a;将nginx 服务器&#xff08;web-filebeat&#xff09;的日志通过filebeat收集之后&#xff0c;存储到缓存服务器kafka&#xff0c;之后logstash到kafka服务器上取出相应日志&#xff0c;经过处理后写入到elasticsearch服务器并在kibana上展示。 一、集群环…

4、创建第一个鸿蒙应用

一、创建项目 此处以空模板为例来创建一个鸿蒙应用。在创建项目前请保持网页的畅通。 1、在欢迎页面点击“Create Project”。 2、左侧默认为“Application”&#xff0c;在“Template Market”中选择空模板&#xff08;Empty Ability&#xff09;&#xff0c;点击“Next” 3…

Redhat Linux v8.2 实时内核环境配置及参数调优

BC-Linux V8.2 实时内核环境配置及参数调优 -------物理机 & 虚拟机 一、前言 本文档包含有关Redhat Linux for Real Time的基本安装和调试信息。许多行业和组织需要极高性能的计算&#xff0c;并且可能需要低且可预测的延迟&#xff0c;尤其是在金融和电信行业中。延迟&…

FAB天车系统OHT 介绍

1.技术背景&#xff1a; “OHT”是Overhead Hoist Transport的简称&#xff0c;是指能够在空中轨道上行驶&#xff0c;并能够通过皮带传动起重机构“直接”进入保管设备或工艺设备的装卸口&#xff0c;应用于工序区内部运输&#xff0c;也应用于工序区间或工厂间运输。 “OHT…

VMware ubuntu 新虚拟机的创建

根据自己指定的路径安装好vm后。 创建新的虚拟机。 记录一下&#xff0c;下次用到别再忘记了。 如需转载&#xff0c;注明出处&#xff01; 点赞收藏关注我 以资鼓励 打开vm 软件&#xff0c;点击创建新的虚拟机 选择典型&#xff0c;点击下一步 选择你的ubuntu镜像iso文件 …

2023测试职业生涯必看系列:手写web自动化测试框架教程 涵盖框架源码+视频教程以及搭建流程

前言 ​ 测试行业现在70%是以手工测试为主&#xff0c;那么只有20%是自动化测试&#xff0c;剩下的10%是性能测试。 有人可能会说&#xff0c;我现在做手工&#xff0c;我为什么要学自动化呢&#xff1f;我去学性能更好性能的人更少&#xff1f; 其实&#xff0c;性能的要求比…

如何利用 cpolar 内网穿透技术实现 U8 用友 ERP 异地访问

文章目录 前言1. 服务器本机安装U8并调试设置2. 用友U8借助cpolar实现企业远程办公2.1 在被控端电脑上&#xff0c;点击开始菜单栏&#xff0c;打开设置——系统2.2 找到远程桌面2.3 启用远程桌面 3. 安装cpolar内网穿透3.1 注册cpolar账号3.2 下载cpolar客户端 4. 获取远程桌面…

Leetcode_203.移除链表元素—C语言

目录 ❣️1.题目❣️ ❣️2.解答❣️ &#x1f49e;方法一&#xff1a;暴力法 &#x1f49e;方法二&#xff1a; 尾插法 &#x1f49e;方法三&#xff1a;哨兵位法 ❣️1.题目❣️ 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.va…

高可用架构设计

1. 引言 软件系统有三个追求&#xff1a;高性能、高并发、高可用&#xff0c;俗称三高。三者既有区别也有联系&#xff0c;门门道道很多&#xff0c;本篇讨论高可用 高可用技术的重要性在于保证系统的连续可用性&#xff0c;提高系统的稳定性和可靠性。它可以应对高并发和大规…

2783爱心公益网站的设计与实现JSP【程序源码+文档+调试运行】

一、摘要 本文将详细介绍一个基于Web的爱心公益网站的设计和实现。该网站包括前台用户模块和后台管理员模块&#xff0c;能够满足用户和管理员的不同需求。通过对用户和管理员的功能需求分析&#xff0c;本文将重点阐述数据库设计和界面设计的关键环节&#xff0c;同时介绍如何…

区块链链游合约系统开发项目模式技术方案

​随着区块链技术的发展&#xff0c;链游合约系统开发逐渐成为了一个备受关注的项目。本文将探讨区块链链游合约系统开发项目的技术方案&#xff0c;包括项目背景、开发目标、技术架构、系统流程、安全措施等方面的内容。 一、项目背景 链游是一种基于区块链技术的游戏&#xf…

初识MySQL(一)(创建数据库,查看删除数据库,备份和恢复,创建表,数据类型)

目录 客户端 ------> MySQL服务(3306) 数据库三层结构-破除MySQL神秘 创建数据库 查看删除数据库 备份恢复数据库 创建表 MySQL常用的数据类型(列类型) ​编辑 ​编辑整型 bit类型 小数类型 字符串类型 日期类型的使用 客户端 ------> MySQL服务(3306) 连接…

nmap原理与使用

kali的命令行中可以直接使用 nmap 命令&#xff0c;打开一个「终端」&#xff0c;输入 nmap 后回车&#xff0c;可以看到 nmap 的版本&#xff0c;证明 nmap 可用。 一、端口扫描 扫描主机的「开放端口」&#xff0c;在nmap后面直接跟主机IP&#xff08;默认扫描1000个端口&am…

Java基础-基础语法

1、概述 一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。 对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff0c;它的状态有&#xff1a;颜色、名字、品种&#xff1b;…

酉矩阵(Unitary Matrix)

对于n阶复数矩阵A&#xff0c;如果&#xff0c;其中表示矩阵A的共轭转置&#xff0c;为单位矩阵&#xff0c;那么就称A为酉矩阵。 对于酉矩阵&#xff0c; 如果酉矩阵的元素都是实数&#xff0c;那么该矩阵就是正交矩阵。

Redis应用之二分布式锁2

一、前言 前一篇 Redis应用之二分布式锁 我们介绍了使用SETNX来实现分布式锁&#xff0c;并且还遗留了一个Bug&#xff0c;今天我们对代码进行优化&#xff0c;然后介绍一下Redisson以及数据库的乐观锁悲观锁怎么用。 二、SetNX分布式锁优化后代码 RedisService.java Inven…

计算机中msvcp140.dll丢失的解决方法,修复msvcp140.dll报错问题

使用电脑的过程中&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“msvcp140.dll丢失”。msvcp140.dll是Microsoft Visual C运行时库的重要组成部分&#xff0c;丢失该文件可能导致程序无法正常运行、系统崩溃等问题。本文将详细介绍msvcp140.dll丢失的原因及…

刨根问底:Java中的“\p{P}”到底是什么意思

问题由来&#xff1a; 在代码中看到了Pattern.compile("\\p{P}")&#xff0c;用来识别符号&#xff0c;但是这个正则表达式却不匹配加号&#xff0c;所以\p{P}到底是什么意思呢 谷歌了一下&#xff0c;找到StackOverflow上有人问了一模一样的问题 可是这个问题被关…

k8s集群搭建(ubuntu 20.04 + k8s 1.28.3 + calico + containerd1.7.8)

环境&需求 服务器&#xff1a; 10.235.165.21 k8s-master 10.235.165.22 k8s-slave1 10.235.165.23 k8s-slave2OS版本&#xff1a; rootvms131:~# lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 20.04.5 LTS Release: …

(三)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁…