栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java中的IO工作机制简析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java中的IO工作机制简析

一、IO基本架构

大体来说,java中的io分为四种;
1、基于字节操作的IO的InputStream和OutputStream,
2、基于字符操作的Writer和Reader,
3、基于磁盘操作的File,
4、基于网络操作的Socket,

对于基于字节操作的输入流和输出流;主要有以下API;
1、read,通过这个方法读入一个字节,并返回读入的字节,在碰到输入流结尾时返回-1;可以传入字节数组作为参数,也可以加上长度限制;
2、transferTo;传入一个输出流,将输入流的数据传到这个输出流中;
3、skip;传入n;跳过n个字节
4、close:关闭输出或输入流;
5、write,读出一个字节数据
6、flush冲刷输出流;

简单来说,因为无论是磁盘传输还是网络传输,最小的存储单位都是字节,所以io操作字节很常用;但是程序中操做的数据常常是字符形式的,所以也提供了直接操作字符的i/o流接口;Reader和Writer,通过传入字符数组;其定义了读取和写入数据字符的方式,而字符传输有以下问题

1、因为传输都是以字节方式进行的,所有对于字符的操作,有必要转化为字节操作;在inputStreamReader中可以指定编码集、或者使用默认的字符集;

2、指定了字符集,如何利用字符的编码的转换;java中使用StreamDecoder类作为从字节到字符解码实现类;使用OutputStream实现一个从字符到字节的编码过程;

3、具体过程:当字符流传入时,需要进行字节的编码,存入整数1234,会被转化为字节 00 00 04 D2等(十六进制标识),而在读出时又会被解码成1234;通过在InputStreamReader选定不同的编码集使用不同的编码方式;使用OutputStream实现解码;最后实现字符的读写;

二、磁盘io读写机制

程序对于磁盘的文件读写方式是通过操作系统中的系统接口实现;因此需要理解Java程序io机制,也需要了解os如何实现io;

1、基于操作系统的io;

因为磁盘由操作系统管理,但是这是属于操作系统的内核程序,所以操作系统的文件操作流程大概如下:
1、首先通过将磁盘数据读到内核态内存,
2、然后在从内核态内存复制到用户态的内存空间;
3、为了提高io效率,操作系统使用缓存机制来实现,即内核态的文件读取后进行一定的缓存;用户态再次访问文件,可以直接去内核态的内存里找数据;

操作系统对于文件io方式有以下几种:
1、标准io方式,即用户态从缓存或者内核态内存拿数据,而内核从磁盘读数据;程序只需要把数据交给内核态即可;

2、直接io方式,常见于各种数据库数据的存储过程,通过用户态直接尝试访问磁盘数据;减少中间内核态的中间存储过滤过程;如数据库的管理过程中,数据库系统明确了应用系统数据那些应缓存,那些应失效;还可以提前预加载;因为将数据缓存在数据库系统中,可能存在反复读取物理磁盘过程;

3、同步io访问, 因为数据的读取和写入基本同步,类似于标准io工作方式,从内核态读取数据,区别在于其程序不会完全托管给内核态,需要监视到数据进入磁盘之后才会返回;

4、异步IO访问,即读写不同步,可能出现的请求托给内核态后,立即开始下一个请求,即多次io处并行状态;

5、内存映射, 操作系统将内存区域和磁盘区域建立一个简单的映射关系,程序访问内核内存时,会自动映射到磁盘内存上;两个地方的数据是同步更改的;

2、Java访问磁盘文件

首先是创建一个File对象,这个对象主要用于作为磁盘文件的一个虚拟对象映射存储在Java中;

如果需要使用File去访问文件,此时会进行一个FileDescribe文件描述符的创建,通过这个文件描述符可以进行磁盘文件的操作;

操作过程,读取的是字符格式,所以需要我们进行一个字节解码过程,使用StreamDecoder解码对象实现,然后创建FileInputStream对象,调用read接口从操作系统拿数据;

File <-> 解码/编码 <-> FileInputStream <-> 操作系统;

3、Java的序列化

序列化即将数据对象转化成一个二进制字节数组,通过保存或转移这个数组达到数据的持久化操作;与之相对是反序列化,将字节数组转回对象;

实现序列化的方式是实现Serializable接口,这是一个空接口,无需实现具体方法,但是推荐是定义一个序列化ID;,因为默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常。
serialVersionUID是用来辅助对象的序列化与反序列化的,原则上序列化后的数据当中的serialVersionUID与当前类当中的serialVersionUID一致,那么该对象才能被反序列化成功。

这个serialVersionUID的详细的工作机制是:
在序列化的时候系统将serialVersionUID写入到序列化的文件中去,当反序列化的时候系统会先去检测文件中的serialVersionUID是否跟当前的文件的serialVersionUID是否一致,如果一直则反序列化成功,否则就说明当前类跟序列化后的类发生了变化,比如是成员变量的数量或者是类型发生了变化,那么在反序列化时就会发生crash,并且回报出错误:

对象在序列化后的二进制数据包括:序列化文件头(声明协议、版本、对象)、类描述、属性项描述、父类信息、属性项的值(如果是对象,递归序列化);

三、网络io模型

数据从一台主机发送到另一台主机需要进行多个复杂步骤;一台主机的应用层通过应用协议如HTTP产生要发送的数据,然后传给表示层,对来自应用层的命令和数据进行解释,对各种语法赋予相应的含义,并按照一定的格式传送给会话层。其主要功能是“处理用户信息的表示问题,如编码、数据格式转换和加密解密”,然后到会话层,网络层,数据链路层,物理层,
最后传到接受主机的物理层,层层向上处理,解封装,最后整理成应用程序需要的数据,交给应用层。

对于网络程序来说,我们需要对于传输层的过程有更多的理解,比如服务器的状态,链接建立;通过查看网络连接的状态,确认传输不正常的原因;一般来说网络传输受到网络带宽、传输距离、TCP拥塞控制的影响;

Java的Socket编程

Socket属于两个主机间的一个交流过程,常基于TCP/IP的套接字;通过Socket实现主机A到主机B的一个连接过程,由于一台主机上有多个程序运行,所以需要通过TCP或者UDP的端口号来实现一个指定一个应用进程;
具体流程:以客户端服务端为例

1、首先创建一个Socket实例,操作系统将为这个Socket分配一个没有使用的端口号,并创建一个包含本地地址、远程地址、和端口号套接字的的数据结构存储信息;
2、然后在Socket实例的构造函数返回前,需要建立TCP的三次握手协议,握手完成后,才能使用Socket实例,否则会有IO异常;
3、服务端创建一个ServerSocket实例、操作系统也会创建一个存储块A,存储监听的端口号和监听的地址;
4、调用accept方法,这个服务端的实例会进入阻塞状态,等待新的请求到来;
5、当新的请求到服务端后,服务端的连接会创建一个新的存储结构B,包括请求内部的端口号地址;这个存储会和服务端前面创建的实例存储结构A做一个关联;等待三次握手完成,则视为建立好连接;
上述5步建立连接;然后是数据传输过程;
6、连接建立完成后,服务端和客户端各有一个Socket实例,可以通过其实例的InputStream和OutputStream实现一个数据的交换;
7、网络IO以字节传输,类似于磁盘间的传输,客户端和服务端也会有一个缓存,写入数据时会有一个RecvQ和SendQ的缓存队列,SendQ接受足够的数据后传给RecvQ,RecvQ队列满了就会暂停写入数据;

下面使用Java实现一个简单的客户端和服务端
以一个Java的Socket实例说明;

public static void main(String[] args) {
        try {
            //一个简易的服务器,可以使用telnet工具连接;在本机上建立了一个8000端口的服务器;
            ServerSocket serverSocket = new ServerSocket(8000);
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
            Scanner in = new Scanner(inputStream, StandardCharsets.UTF_8);
            PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8), true);

            printWriter.println("hello!这里会返回你输入的结果,exit退出");

            boolean done = false;
            while(!done&&in.hasNext()){
                String line = in.nextLine();
                printWriter.println("输入的字符为" + line);
                if(line.trim().equals("exit")) done = true;
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//上述实例只是单线程下的服务,如果需要多人访问,
//则需要使用到多线程的相关实现

事实上除了Socket,Java还提供了URLURI类用于获取连接中的数据,他们使用连接建立了一个流式的数据,此外使用URLConnection类,可以获取请求的更多控制信息;
Java9以后引入了HttpClient类,用于提供对Http/2的支持以及更便捷的客户端API;可以使用它作为客户端发出请求;

四、NIO、AIO以及BIO 五、io优化

1、性能检测
2、提升IO性能
1)、增加缓存
2)、优化磁盘管理系统
3)、设计合理的磁盘存储数据块
4)、应用合理的RAID策略
服务器系统网络io优化
1)、减少网络交互次数
2)、减少网络传输数据量大小
3)、减少编码和解码
4)、根据不同交互场景选择不同的io组合方式,如同步阻塞,同步非阻塞

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/424349.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号