Java IO,BIO、NIO、AIO

news2024/11/16 23:46:46

操作系统中的 I/O
      以上是 Java 对操作系统的各种 IO 模型的封装,【文件的输入、输出】在文件处理时,其实依赖操作系统层面的 IO 操作实现的。【把磁盘的数据读到内存种】操作系统中的 IO 有 5 种:
阻塞、
非阻塞、【轮询】
异步、
IO复用、【多个进程的 IO 注册到管道上】
信号驱动 IO
 

1.传统的IO流会进行阻塞操作、一个字节一个字节操作
2.NIO提供了一个区域块的 数据映射、 异步的IO流操作
3.通道是 用于获取流中数据 ===>【channel获取的是传统IO流中的数据,读写】
4.缓冲区是 用于保存通道中的数据- ===>【buffer获取的是channel中的数据,读写】

 传统IO操作请看IO操作
IO和NIO的区别
IO:1.面向流(Stream) 2.阻塞IO(Blocking IO)
NIO:1.面向缓冲区 2.非阻塞IO(NO Blocking IO)3. 选择器(Selectors)

(一)引言
IO流是Java中比较难理解的一个知识点,但是IO流在实际的开发场景中经常会使用到,比如Dubbo底层就是NIO进行通讯。本文将介绍Java发展过程中出现的三种IO:BIO、NIO以及AIO,重点介绍NIO。

(二)什么是BIO
BIO即同步阻塞IO,实现模型为一个连接就需要一个线程去处理。这种方式简单来说就是当有客户端来请求服务器时,服务器就会开启一个线程去处理这个请求,即使这个请求不干任何事情,这个线程都一直处于阻塞状态。

BIO模型有很多缺点,最大的缺点就是资源的浪费。想象一下如果QQ使用BIO模型,当有一个人上线时就需要一个线程,即使这个人不聊天,这个线程也一直被占用,那再多的服务器资源都不管用。

同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。
异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于 异步调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。
非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。
 

 BIO (Blocking I/O) 同步阻塞
     服务端创建一个 ServerSocket , 然后就是客户端用一个Socket 去连接服务端的那个 ServerSocket, ServerSocket 接收到了一个的连接请求就创建一个Socket和一个线程去跟那个 Socket 进行通讯。接着客户端和服务端就进行阻塞式的通信,客户端发送一个请求,服务端 Socket 进行处理后返回响应,在响应返回前,客户端那边就阻塞等待,什么事情也做不了。
     这种方式的缺点, 每次一个客户端接入,都需要在服务端创建一个线程来服务这个客户端,这样大量客户端来的时候,就会造成服务端的线程数量可能达到了几千甚至几万,这样就可能会造成服务端过载过高,最后崩溃死掉。

(三)BIO代码实践

我们通过socket模拟BIO的实现逻辑

首先建立Server,建立一个ServerSocket对象,绑定端口,然后等待连接,如果连接成功就新建一个线程去处理连接。

server服务端:两种情况:

1.单线程处理多个客户端

2.多线程处理多个客户端 ,也就是服务端接受到客户端的socket就开启一个线程去处理.

package com.cn.jettech.jettoproimage.controller.imagecontroller01.imagecontroller01.io.bio;


import java.io.IOException;
import java.net.*;

/**
 * BIO即同步阻塞IO,实现模型为一个连接就需要一个线程去处理
 *
 **/
public class BIOServer {
    private static Socket socket = null;
    private static int thread_queue_size = 100;
    private static volatile int i = 0;
    private static int server_port = 8080;
    private static String server_ip = "192.168.0.17";
    public static void main(String[] args) throws  Exception{
        //ServerSocket serverSocket = new ServerSocket(BIOServer.server_port,BIOServer.thread_queue_size,InetAddress.getByName(BIOServer.server_ip));
        ServerSocket serverSocket = new ServerSocket();
        //该选项用来决定如果网络上仍然有数据向旧的ServerSocket传输数据,是否允许新的ServerSocket绑定到与旧的ServerSocket同样的端口上,
        //该选项的默认值与操作系统有关,在某些操作系统中,允许重用端口,而在某些系统中不允许重用端口。
        //当ServerSocket关闭时,如果网络上还有发送到这个serversocket上的数据,这个ServerSocket不会立即释放本地端口,
        //而是等待一段时间,确保接收到了网络上发送过来的延迟数据,然后再释放端口。
        //值得注意的是,public void setReuseAddress(boolean on) throws SocketException必须在ServerSocket还没有绑定到一个本地端口之前使用,
        //否则执行该方法无效。此外,两个公用同一个端口的进程必须都调用serverSocket.setReuseAddress(true)方法,才能使得一个进程关闭ServerSocket之后,
        //另一个进程的ServerSocket还能够立刻重用相同的端口。
        //serverSocket.setReuseAddress(true);
        //SO_RCVBUF 表示 Socket 的用于输入数据的缓冲区的大小. 一般说来, 传输大的连续的数据块(基于HTTP 或 FTP 协议的通信) 可以使用较大的缓冲区,
        //这可以减少传输数据的次数, 提高传输数据的效率. 而对于交互频繁且单次传送数据量比较小的通信方式(Telnet 和 网络游戏), 则应该采用小的缓冲区,
        //确保小批量的数据能及时发送给对方. 这种设定缓冲区大小的原则也同样适用于 Socket 的 SO_SNDBUF 选项.
        //serverSocket.setReceiveBufferSize(10000);
        //SO_TIMEOUT表示ServerSocket的accept()方法等待客户连接的超时时间,以毫秒为单位。如果SO_TIMEOUT的值为0,表示永远不会超时,这是SO_TIMEOUT的默认值。
        //当服务器执行ServerSocket的accept()方法时,如果连接请求队列为空,服务器就会一直等待,直到接收到了客户连接才从accept()方法返回。如果设定了超时时间,
        //那么当服务器等待的时间超过了超时时间,就会抛出SocketTimeoutException,它是InterruptedException的子类。
        //当底层的Socket实现不支持SO_TIMEOUT选项时,这两个方法将抛出SocketException例外。不能将timeout设为负数,
        //否则setSoTimeout方法将抛出IllegalArgumentException例外。
        //serverSocket.setSoTimeout(10000);
        //backlog : 输入连接指示(对连接的请求)的最大队列长度被设置为 backlog 参数。如果队列满时收到连接指示,则拒绝该连接。
        //1. backlog参数必须是大于 0 的正值。如果传递的值等于或小于 0,则假定为默认值。
        //2. 经过测试这个队列是按照FIFO(先进先出)的原则。
        //3. 如果将accept这个函数放在一个循环体中时,backlog参数也不会有什么作用。或者简单的讲运行ServerSocket的这个线程会阻塞时,无论是在accept,还是在read处阻塞,这个backlog参数才生效。
        serverSocket.bind(new InetSocketAddress(BIOServer.server_port),BIOServer.thread_queue_size);
//        System.out.println(serverSocket.isBound() && !serverSocket.isClosed());
//        if(serverSocket.isBound()) {
//            System.out.println(serverSocket.getLocalPort());
//            System.out.println(serverSocket.getInetAddress().getHostAddress());
//        }
        while (true) {
            //等待连接  阻塞
            ++i;
            System.out.println("等待客户端的连接");
            socket = serverSocket.accept();
            System.out.println("服务端已经获取到了客户端你的第"+i+"个socket,连接成功"+socket.getRemoteSocketAddress());
            try {
                Thread.sleep(1000); //thread_queue_size参数是模拟请求的队列池子大小---单线程模式下,这样客户端请求过大服务器不予处理。
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            //System.in.read();
            //Thread thread= new Thread(new MyRun(socket));  //多线程处理客户端
            //thread.start();
        }
    }
}
class MyRun implements  Runnable {
    private Socket socket;
    public MyRun(Socket socket) {
        this.socket = socket;
    }
    public MyRun() {
    }
    public void run() {
        byte[] bytes=new byte[33];
        try {
            System.out.println("服务端等待读数据");
            int length=socket.getInputStream().read(bytes);
            System.out.println("读到客户端传过来的数据的长度是:" +length);
//            System.out.println("服务端每次处理的最大数据是:" +bytes.length);
//            System.out.println("============第一段"+new String(bytes,0,6)+"=============");
            System.out.println("======"+Thread.currentThread().getName()+"======读取整体"+new String(bytes,0,length)+"=============");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                socket.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

client客户端:属于真正的客户端,因为数多线程模拟的客户端

package com.cn.jettech.jettoproimage.controller.imagecontroller01.imagecontroller01.io.bio;
import java.io.IOException;
import java.net.Socket;
public class BIOClient {
    private static int socket_size = 100;
    private static int server_port = 8080;
    private static String server_ip = "192.168.0.17";
    public static void main(String[] args) throws Exception{
        Socket[] client = new Socket[BIOClient.socket_size];
        for (int i = 0; i < BIOClient.socket_size; i++) {
            final int index = i;
            new Thread(()->{
                try {
                    client[index] = new Socket(BIOClient.server_ip,BIOClient.server_port);
//                    client[index].getOutputStream().write("我是第".getBytes());
//                    client[index].getOutputStream().write(index);
//                    client[index].getOutputStream().write("客户端过来的数据".getBytes());
                    System.out.println("第 " + (index+1) + "客户端连接成功");
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            },String.valueOf(i)).start();
        }
    }
}

BIO是阻塞的,如果没有多线程,BIO就需要一直占用CPU,而NIO则是非阻塞IO,NIO在获取连接或者请求时,即使没有取得连接和数据,也不会阻塞程序。NIO的服务器实现模式为一个线程可以处理多个请求(连接)。也就是说BIO服务端多线程也可以处理多个客户端,但是这样服务端线程数量会增多性能会下降。

(四)什么是NIO
BIO是阻塞的,如果没有多线程,BIO就需要一直占用CPU,而NIO则是非阻塞IO,NIO在获取连接或者请求时,即使没有取得连接和数据,也不会阻塞程序。NIO的服务器实现模式为一个线程可以处理多个请求(连接)。

NIO有几个知识点需要掌握,Channel(通道),Buffer(缓冲区), Selector(多路复用选择器)。

Channel既可以用来进行读操作,又可以用来进行写操作。NIO中常用的Channel有FileChannel
、SocketChannel、ServerSocketChannel、DatagramChannel。

Buffer缓冲区用来发送和接受数据。

Selector 一般称为选择器或者多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。在javaNIO中使用Selector往往是将Channel注册到Selector中。

下面我通过代码的方式模拟javaNIO的运行流程。

(五)NIO代码实践
首先贴上NIO的实践代码:

NIO服务端详细的执行过程是这样的:

1、创建一个ServerSocketChannel和Selector,然后将ServerSocketChannel注册到Selector上

2、Selector通过select方法去轮询监听channel事件,如果有客户端要连接时,监听到连接事件。

3、通过channel方法将socketchannel绑定到ServerSocketChannel上,绑定通过SelectorKey实现。

4、socketchannel注册到Selector上,关心读事件。

5、Selector通过select方法去轮询监听channel事件,当监听到有读事件时,ServerSocketChannel通过绑定的SelectorKey定位到具体的channel,读取里面的数据。


 

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

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

相关文章

STM32的SDIO功能框图及SDIO结构体

目录 STM32的SDIO功能框图及SDIO结构体 STM32的SDIO功能框图 SDIO适配器 命令路径 CPSM状态机 数据路径 DPSM状态机 数据FIFO 适配器寄存器 SDIO相关结构体 SDIO初始化结构体 SDIO命令初始化结构体 SDIO数据初始化结构体 STM32的SDIO功能框图及SDIO结构体 STM32的…

3d软件动物生活习性仿真互动教学有哪些优势

软体动物是一类广泛存在于海洋和淡水环境中的生物&#xff0c;其独特的形态和生活习性给学生带来了新奇和有趣的学习主题&#xff0c;为了方便相关专业学科日常授课教学&#xff0c;web3d开发公司深圳华锐视点基于真实的软体动物&#xff0c;制作软体动物3D虚拟展示系统&#x…

发点实用的快捷键(mac

切换输入法&#xff1a;ctrlspace /ctrloptionspace&#xff08;更快捷 切换网页&#xff1a; shifttab 切换应用界面&#xff1a;alttab 关闭页面&#xff1a;altw 搜索&#xff1a;altspace 展示mac隐藏文件&#xff1a; Commangshift . (点) 以下是一些浏览器快捷键&am…

【初阶C语言】认识和使用函数

1. 函数是什么 2. 库函数 3. 自定义函数 4. 函数参数 5. 函数调用 6. 函数的嵌套调用和链式访问 7. 函数的声明和定义 8. 函数递归 一、什么是函数 在数学中有函数&#xff0c;在C语言中也有函数&#xff0c;我们直接先给出一个定义&#xff1a; 在基维百科中函数被定义为子程…

MyBatisPlus入门到精通-1

MyBatisPlus(简称MP) 这篇博客主要讲解用MyBatisPlus进行三层架构中Dao层的开发 以这个为目的来进行我们的学习 我们会先通过一个概述和入门案例进行快速上手 之后我们再通过对我们原先的案列的问题进行分析 来进一步了解MP操作数据库的知识 快速入门 MP简介 MP是国人开发的…

HEVC并行处理技术介绍

h265 相比 h264 的复杂度 复杂度体现 ○ h265 帧内预测模式增多&#xff0c;h265 包含角度预测、DC 预测、平面模式等 35 种预测模式&#xff0c;远超 h264 的 17 种模式&#xff0c;帧内模式选择的复杂度大大增加&#xff1b; ○ h265 的区域划分方式更加多样化&#xff0c;提…

聊聊STM32串口通讯的话题

STM32 微控制器系列提供了多个串口模块&#xff0c;用于实现串口通讯。其中&#xff0c;STM32HAL 库中的 UART 驱动模块提供了一套方便易用的函数接口&#xff0c;可以用来配置和操作串口。 串口通讯是一种常见的数据传输方式&#xff0c;可以实现微控制器与外部设备或其他微控…

Jetbrains 2023.2教程

IDEA 2023.2 激活演示 Pycharm 2023.2 激活演示 WebStorm 2023.2 激活演示 Clion 2023.2 激活演示 DataGrip 2023.2 PhpStorm 2023.1.4 激活演示&#xff08;2023.2尚未发布&#xff09; RubyMine 2023.2 激活演示 获取方式 仔细看每一个工具演示的图片 本文由 mdnice …

深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析

在 Elasticsearch 中&#xff0c;function_score 可以让我们在查询的同时对搜索结果进行自定义评分。 function_score 提供了一系列的参数和函数让我们可以根据需求灵活地进行设置。 近期有同学反馈&#xff0c;function_score 的相关参数不好理解&#xff0c;本文将深入探讨 f…

2023软件设计师中级备考经验分享(文中有资料链接分享)

先摊结论吧&#xff0c;软考中级设计师备考只是备考半个月&#xff08;期间还摆烂了几天&#xff09;&#xff0c;然而成绩如下&#xff1a; 我自己都没想到会这么好的成绩。。。 上午题&#xff1a;推荐把软考通APP里的历年真题刷3-4遍&#xff0c;直接刷真题&#xff0c;然后…

TimescaleDB多节点功能概述

如果您有更大的pb级工作负载&#xff0c;则可能需要多个TimescaleDB实例。TimescaleDB多节点允许您运行和管理数据库集群&#xff0c;这可以为您提供更快的数据摄取&#xff0c;以及对大型工作负载响应更快、更高效的查询。 多节点架构 多节点TimescaleDB允许您将多个数据库连…

【学习笔记】视频检测方法调研

目录 1 引言2 方法2.1 视频目标跟踪2.1.1 生成式模型方法2.1.2 判别式模型方法2.1.2.1 基于相关滤波跟踪2.1.2.2 基于深度学习跟踪 2.2 视频异常检测2.2.1 基于重构方法2.2.2 基于预测方法2.2.3 基于分类方法2.2.4 基于回归方法 2.3 深度伪造人脸视频检测2.3.1 基于RNN时空融合…

UG NX二次开发(C++)-利用UFun函数获取曲面上等参数的点

文章目录 1、前言2、叶片的三维模型3、利用UFun函数获取曲面的三种方法3.1 利用UF_Modl来实现3.2 利用UF_Point来实现3.2 利用UF_So来实现4、结果展示1、前言 在多轴加工过程中(尤其是薄壁零件),由于需要根据工件表面的几何信息以获取切触点,然后生成刀位轨迹,本文以叶片…

UEditor 百度富文本编辑器使用 遇到问题

小小吐槽 碰到前后不分离项目&#xff0c;富文本使用的UEdtior UEditor 点击上传图片转base64 在ueditor.all.js文件中找到这个 callback()函数 这里使用根据图片的url转成base64 UEditore 粘贴图片转base64 UEditor回显图片&#xff08;base64&#xff09; 把ueditor.all…

【Docker】制作Docker私有仓库

文章目录 1. 安装私有镜像仓库2. 镜像仓库可视化3. 参考资料 1. 安装私有镜像仓库 由于之后我们需要推送到私有镜像仓库&#xff0c;我们预先安装好&#xff0c;使用的是Docker公司开发的私有镜像仓库Registry。 下载Registry的Docker镜像&#xff1b; docker pull registry:2使…

【Django+Vue】英文成绩管理平台--20230727

项目需求 能够满足大部分核心需求&#xff08;标绿&#xff09;&#xff1a;报表部分应该比较难。 项目地址 前端编译 https://gitlab.com/m7840/toeic_vue_dist Vue源码 https://gitlab.com/m7840/toeic_vue Django源码 https://gitlab.com/m7840/toeic_python 项目…

docker部署nextcloud+onlyoffice详细教程【外置MySQL】

一、部署环境 系统版本&#xff1a;CentOS Linux release 7.9.2009 (Core) 配置&#xff1a;16C 16G MySQL&#xff1a;5.7.20 二、部署过程 1、安装docker和docker-compose 请百度 2、创建数据库和用户&#xff0c;并授权 因为我使用外置MySQL&#xff0c;所以需要先创…

从零开始学Docker(一):Docker的安装部署

前述&#xff1a;本次学习与整理来至B站【Python开发_老6哥】老师分享的课程&#xff0c;有兴趣的小伙伴可以去加油啦&#xff0c;附链接 Linux 环境&#xff1a;RockyLinux 9 版本管理 Docker引擎主要有两个版本&#xff1a;企业版&#xff08;EE&#xff09;和社区版&#…

C语言爱心代码

代码&#xff1a; #include <stdio.h> int main() { int i, j, k, l, m; char c3; //ASCII码里面 3 就是一个字符小爱心 for (i1; i<5; i) printf("\n"); //开头空出5行 for (i1; i<3; i) { //前3行中间有空隙分开来写 for (j1; j<32-2*i; j) p…

计算机图形学十三加速结构

加速光线追踪 摘要 在上一节中&#xff0c;我们介绍了whited-style 光线追踪的原理&#xff0c;以及实现细节。相比与光栅化中所使用的blinn-phong模型&#xff0c;光线追踪显著提升了图像质量&#xff0c;但是随之而来的问题是渲染速度过慢。因为在判断光线与场景交点的时候…