IO 与 NIO

优质博文:IT-BLOG-CN

一、阻塞IO / 非阻塞NIO

阻塞IO:当一条线程执行read()或者write()方法时,这条线程会一直阻塞直到读取到了一些数据或者要写出去的数据已经全部写出,在这期间这条线程不能做任何其他的事情。

非阻塞NIONIO与原有的IO有同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的、基于通道的操作。NIO将以更加高效的方式进行文件读写操作。JAVA NIO的核心在于:通道Channel和缓冲区Buffer。通道表示打开IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区。对数据进行处理。

二、传统IO测试代码如下

当出现accept()read()等方法是就会阻塞。

/**
 * 传统socket服务端
 * @author -zhengzx-
 */
public class OioServer {

    @SuppressWarnings("resource")
    public static void main(String[] args) throws Exception {

        //创建socket服务,监听10101端口
        ServerSocket server=new ServerSocket(10101);
        System.out.println("服务器启动!");
        while(true){
            //获取一个套接字(阻塞)
            final Socket socket = server.accept();//(测试时可以通过:telnet 127.0.0.1 10101。进行测试)
            System.out.println("来个一个新客户端!");
            //业务处理
            handler(socket);
        }
    }

    /**
     * 读取数据
     * @param socket
     * @throws Exception
     */
    public static void handler(Socket socket){
            try {
                byte[] bytes = new byte[1024];
                InputStream inputStream = socket.getInputStream();

                while(true){
                    //读取数据(阻塞)
                    int read = inputStream.read(bytes);
                    if(read != -1){
                        System.out.println(new String(bytes, 0, read));
                    }else{
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                try {
                    System.out.println("socket关闭");
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    }
}

三、阻塞 IO解决办法

可以通过线程池创建多线程,为每一次连接创建一个新的线程来执行。问题是对于长连接而言,线程过多时会严重消耗系统资源导致性能下降。比较适合短连接的应用。

public static void main(String[] args) throws Exception {
    //创建线程池(可以通过线程解决阻塞问题、问题:每次连接都会创建一个线程,特别是长连接时特别消耗系统资源)
    ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    //创建socket服务,监听10101端口
    ServerSocket server=new ServerSocket(10101);
    System.out.println("服务器启动!");
    while(true){
        //获取一个套接字(阻塞)
        final Socket socket = server.accept();//(测试时可以通过:telnet 127.0.0.1 10101。进行测试)
        System.out.println("来个一个新客户端!");
        newCachedThreadPool.execute(new Runnable() {

            @Override
            public void run() {
                //业务处理
                handler(socket);
            }
        });
    }
}

四、NIO 的非阻塞模式

Java NIO有阻塞模式和非阻塞模式,阻塞模式的NIO除了使用Buffer存储数据外和IO基本没有区别,允许一条线程从Channel中读取数据,通过返回值来判断buffer中是否有数据,如果没有数据,NIO不会阻塞,因为不阻塞这条线程就可以去做其他的事情,过一段时间再回来判断一下有没有数据。

*SelectorsJava NIOselectors允许一条线程去监控多个channels的输入,你可以向一个selector上注册多个channel,然后调用selectorselect()方法判断是否有新的连接进来或者已经在selector上注册时channel是否有数据进入。selector的机制让一个线程管理多个channel变得简单。

五、NIO示例代码如下

客户端使用SocketChannel,服务端使用ServerSocketChannel获取通道

public class NIOServerSocket {
    //定义一个socket入口
    private ServerSocketChannel serverSocket;
    //定义一个监听器
    Selector selector;
    public static void main(String[] args) throws IOException {
        NIOServerSocket nio =new NIOServerSocket();
        nio.initServer(8000);
        nio.listen();
    }
    public void initServer(int port) throws IOException {
        //获取一个serverSocket通道
        serverSocket = ServerSocketChannel.open();
        //设置为非阻塞状态(分为阻塞和非阻塞两种情况)
        serverSocket.configureBlocking(false);
        //将通道对应的serverSocketChannel绑定到端口上
        serverSocket.socket().bind(new InetSocketAddress(port));
        //获取一个通道管理器
        this.selector = Selector.open();
        //将通道管理器与通道进行绑定,并赋值SelectionKey.OP_ACCEPT事件
        //注册后,当事件到达后,select.select()会返回,如果没有返回,就一直阻塞。
        serverSocket.register(selector, SelectionKey.OP_ACCEPT);
    }
    public void listen() throws IOException {
        System.out.println("服务器启动");
        //轮询访问select.select()
        while(true) {
            //当事件到达时返回,否则一直阻塞
            Channel channel = selector.select();
            //获取selector中选中项的迭代器,相中的项为注册事件。
            Iterator<SelectionKey> iterator = this.selector.selectedKeys().iterator();
            while(iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                //删除已选的key,防止重复处理
                iterator.remove();
                handler(selectionKey);
            }
        }
    }
    public void handler(SelectionKey key) throws IOException {
        if(key.isAcceptable()) {
            handlerAccept(key);
        }else if(key.isReadable()) {
            handlerRead(key);
        }
    }
    public void handlerAccept(SelectionKey key) throws IOException {
        //获取以有的通道
        ServerSocketChannel channel = (ServerSocketChannel) key.channel();
        //获取和客户端连接的通道
        SocketChannel accept = channel.accept();
        //设置为非阻塞
        accept.configureBlocking(false);
        // 在这里可以给客户端发送信息哦
        System.out.println("新的客户端连接");
        //连接成功之后,为了读取客户端传送的消息,需要设置读权限
        accept.register(selector, SelectionKey.OP_READ);
    }
    public void handlerRead(SelectionKey key) throws IOException {
        //服务器可读取消息,获取事件发生的Socket通道
        SocketChannel channel = (SocketChannel) key.channel();
        //创建读取内容的缓存区buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int read = channel.read(buffer);
        if(read > 0) {
            byte[] array = buffer.array();
            String msg = new String(array).trim();
            System.out.println("服务端收到信息:" + msg);

            //会写
            ByteBuffer byteBuffer = ByteBuffer.wrap("success".getBytes());
            channel.write(byteBuffer);
        }else {
            System.out.println("客户端关闭");
            key.cancel();
        }
    }
}

六 、selector.select()

selector.select()虽阻塞,但可以通过selector.wakeup()唤醒selector执行,也可以通过selector.select(int timeout)设置时间限制,timeout时间后唤醒 selector

七、NIO提高性能

添加多线程,一个线程对应一个selector,端口的监听可以单独创建一个selector。(既Netty的工作原理)

总结: NIO允许你用一个单独的线程或几个线程管理很多个channels(网络的或者文件的),代价是程序的处理和处理IO相比更加复杂。如果你需要同时管理成千上万的连接,但是每个连接只发送少量数据,例如一个聊天服务器,用NIO实现会更好一些,相似的,如果你需要保持很多个到其他电脑的连接,例如P2P网络,用一个单独的线程来管理所有出口连接是比较合适的。

IO:如果你只有少量的连接但是每个连接都占有很高的带宽,同时发送很多数据,传统的IO会更适合

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

大数据技术(一)

大数据技术概述 大数据技术层面及其功能 数据采集与预处理 利用ETL(extract-transform-load)工具将分布的、异构数据源中的数据&#xff0c;如关系数据、平面数据文件等&#xff0c;抽取到临时中间层后进行清洗、转换、集成&#xff0c;最后加载到数据仓库或数据集市中&…

单例模式及应用场景

如果希望自己的代码更优雅、可维护性更高以及更简洁&#xff0c;往往离不开设计模式这一解决方案。 在JS设计模式中&#xff0c;最核心的思想&#xff1a;封装变化&#xff08;将变与不变分离&#xff0c;确保变化的部分灵活&#xff0c;不变的部分稳定&#xff09;。 那么来…

RISC-V特权架构 - CSR寄存器

RV32/64 特权架构 - CSR寄存器 1 CSR地址空间2 CSR定义2.1 用户级2.2 监管级2.3 超级监管级2.4 机器级 3 CSR访问3.1 CSRRW3.2 CSRRS3.3 CSRRC3.4 CSRRWI3.5 CSRRSI3.6 CSRRCI 本文属于《 RISC-V指令集基础系列教程》之一&#xff0c;欢迎查看其它文章。 1 CSR地址空间 RISC&…

[笔记] 使用 Java Swing 实现一个简单的窗口

Java Swing 是一个用于构建图形用户界面&#xff08;GUI&#xff09;的Java库&#xff0c;它提供了丰富的组件和工具&#xff0c;用于创建交互式的桌面应用程序。Swing 是 Java Foundation Classes&#xff08;JFC&#xff09;的一部分&#xff0c;它是 Java 平台的一种标准用户…

超全面!Linux学习资料大合集,21套从入门到进阶,看这篇就够了

本文将为那些渴望学习Linux&#xff0c;但又缺乏相应资料和方向的朋友&#xff0c;提供21套Linux优质资料&#xff0c;包含入门到进阶&#xff0c;希望能对大家有所帮助。 此合集内容及其丰富&#xff0c;涉及方面颇多&#xff0c;不仅适合Linux入门学习的朋友&#xff0c;运维…

麻省理工最新开发AI模型,让机器人实现自主规划路线

文 | BFT机器人 麻省理工学院的研究人员独具匠心地应用了人工智能来解决仓库中的机器人路径规划问题&#xff0c;以此缓解交通拥堵的难题。据该学院介绍&#xff0c;他们的团队开发了一种深度学习模型&#xff0c;其效率比传统的强随机搜索方法高出近四倍&#xff0c;极大地提…

彻底剖析激光-视觉-IMU-GPS融合SLAM算法:理论推导、代码讲解和实战

自主导航是机器人与自动驾驶的核心功能&#xff0c;而SLAM技术是实现自主导航的前提与关键。现有的机器人与自动驾驶车辆往往会安装激光雷达&#xff0c;相机&#xff0c;IMU&#xff0c;GPS等多种模态的传感器&#xff0c;而且已有许多优秀的激光SLAM与视觉SLAM算法。但是每种…

nginx主动检测后端健康模块

一、前言 nginx也有自带的后端检测模块ngx_http_upstream_module&#xff0c;该模块可以做到基本的健康检查&#xff0c;因为该健康检查是被动的&#xff0c;当nginx有请求后&#xff0c;才会对后端服务进行健康检测&#xff0c;当检测到有故障时会将这个请求转发到正常的后端服…

云计算市场,从追求“规模制胜”到走向“用户分化”

文|智能相对论 作者|叶远风 通常来说&#xff0c;价格战放到任何行业&#xff0c;都不是什么好事。 如今&#xff0c;作为曾经的前沿技术创新&#xff0c;云计算行业正在被迫走入价格战的阴霾当中&#xff0c;引发业界担忧。 ECS&#xff08;云服务器&#xff09;最高降36%…

数据库之间数据迁移工具datax

简介 DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS, databe…

2024.3.1 网络编程

1.思维导图 2.TCP机械臂测试 程序代码&#xff1a; #include <myhead.h> #define SER_IP "192.168.125.254" //服务器端IP #define SER_PORT 8888 //服务器端端口号#define CLI_IP "192.168.199.131" //客户端IP …

C++_数据类型_字符串型

作用 用于表示一串字符 两种风格 C风格字符串&#xff1a;char 变量名[] "字符串值” 示例 注意 C风格的字符串要用双括号括起来 C风格字符串&#xff1a;string 变量名 "字符串值” 注意 用C风格字符串的时候&#xff0c;要包含这个头文件#include <st…

基于React低代码平台开发:构建高效、灵活的应用新范式

文章目录 一、React与低代码平台的结合优势二、基于React的低代码平台开发挑战三、基于React的低代码平台开发实践四、未来展望《低代码平台开发实践&#xff1a;基于React》编辑推荐内容简介作者简介目录前言为什么要写这本书 读者对象如何阅读本书 随着数字化转型的深入&…

GraphView实时图像刷新

代码&#xff1a; GraphViewTest::GraphViewTest(QWidget *parent): QWidget(parent) {ui.setupUi(this);m_bll BllData::getInstance();connect(m_bll, &BllData::returnImgDataSignal, this, &GraphViewTest::returnImgDataSlot);ui.graphicsView->setHorizonta…

RabbitMQ分享

RabbitMQ遵循AMQP协议&#xff0c;自身采用Erlang RabbitMQ工作模式 生产者发消息&#xff0c;启动多个消费者实例来消费消息&#xff0c;每个消费者仅消费部分信息&#xff0c;可达到负载均衡的效果。 RabbitMQ三种常用交换机类型&#xff1a; 交换机主要起调度分发作用。 …

正弦波生成的傅里叶级数展开法

目录&#xff1a; 一、积分法 二、傅里叶级数展开法 附录、常见波形的傅里叶级数 一、积分法 通过对三角波进行积分&#xff0c;即可得到正弦波。有关内容移步&#xff1a;运算放大器应用汇总1之六、积分电路。 下面对傅里叶级数展开法进行描述。 二、傅里叶级数展开法 三…

高通QNX基线编译原理

下面代码以高通智驾平台为例。 1 QNX应用程序编译原理 在高通提供的qnx开发包中,qnx的内核已经由qnx所提供,所以qnx的编译,其实就是大量应用程序的编译,以及最后利用buildfile文件,把内核,库文件以及应用程序打包在一起的过程。 1.1 qnx的工程目录 应用程序的编译,可…

Tomcat服务部署优化

目录 一.Tomcat的基本内容 1.概念 2.构成 &#xff08;1&#xff09;web容器 &#xff08;2&#xff09;servlet容器&#xff08;catalina&#xff09; &#xff08;3&#xff09;JSP容器 3.Tomcat顶层架构 &#xff08;1&#xff09;Tomcat中最顶层的容器是Server&…

运放设计选型中关注的参数-运算放大器选型参数

1、直流增益&#xff08;AVD&#xff09; 直流增益是运放最重要一个属性之一&#xff0c;其定义为输出电压的变化与输入电压变化之比值&#xff0c;通常用V/mV表示这个比值&#xff0c;例如&#xff0c;增益为30000&#xff0c;可表示为30V/mV&#xff0c;有些地方也会把增益用…

盲行的守护者:盲人应用的温暖相伴

作为一个视障人士&#xff0c;我常常对前方的未知感到迷茫。每一步都像是踏入未知的领域&#xff0c;恐惧与不安时刻伴随着我。然而&#xff0c;一款名为蝙蝠避障的盲人手机应用&#xff0c;成为了我前行的明灯。 在这款盲人手机应用的帮助下&#xff0c;我拥有了新的探知世界的…