深入I/O学习记录-01java I/O工作机制

来源:互联网 发布:java工作内容 编辑:程序博客网 时间:2024/06/05 15:50

概述

I/O在web中随处可见,网络传输,文件读取

java I/O分类

基于字节操作的I/O : inputStream 和 outputStream

基于字符操作的I/O : Writer 和 Reader

基于磁盘操作的I/O : File

基于网络操作的I/O : Socket

这里分为四组,前两组是数据传输的格式,后两组是数据传输的方式。虽然Socket并不在I/O包下,但是I/O的核心就是把什么样子(格式)的数据传递(传输)到什么地方。

基于字节的I/O操作

InputStream 和 OutputStream 是两个 abstact 类,对于字节为导向的 stream 都扩展这两个基类

InputStream 的层次结构图
这里写图片描述

OutputStream 的层次结构图

这里写图片描述

基于字符的I/O操作

不管是磁盘传输还是网络传输,最小的存储单元都是字节,而不是字符,所有I/O操作的都是字节而不是字符,但是程序中我们操作的都是字符形式的,所以就存在直接读写字符的I/O接口

Reader 的层次结构图

这里写图片描述

Writer 的层次结构图

这里写图片描述

这些类的具体用法可以查看jdk api,这个网址也有很详细的说明
http://blog.csdn.net/jiangwei0910410003/article/details/22376895

字节与字符之间的转换

InputStreamReader 是读操作 从字节到字符转换之间的桥梁 , InputStream 到 Reader的过程需要指定编码字符集,否则以系统默认字符集进行编码。 StreamDecoder正式从字节到字符的实现类.
下面代码片段是 InputStreamReader 的 构造方法源码片段

public InputStreamReader(InputStream in) {        super(in);        try {            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object        } catch (UnsupportedEncodingException e) {            // The default encoding should always be available            throw new Error(e);        }    }

OutputStreamReader 同样是写操作 从字节到字符转换之间的桥梁

磁盘I/O的工作机制

访问文件的方式

读取和写入文件I/O操作,都是调用操作系统提供的接口来完成的,因为磁盘是由操作系统来进行管理的,应用程序访问物理设备,只能通过调用操作系统提供的接口来进行读写操作。而只要是通过操作系统提供的接口进行调用,就会存在内核空间地址与用户空间地址切换的问题。这是操作系统为了系统安全,将内核程序运行的内存空间与应用程序运行的内存空间进行隔离造成的。这样做虽然保证了内核程序运行的安全性,但是也产生了内核空间与应用程序空间进行复制的问题.

标准访问文件的方式

读操作:当应用程序进行读取时,调用操作系统提供的read接口,操作系统会检查内核缓存中是否存在,如果存在直接返回缓存中的数据。如果没有,读取磁盘,缓存到内核缓存,然后在复制给应用程序
写操作:应用程序调用操作系统提供的writer接口,将数据从应用程序空间地址复制到内核地址的空间缓存中,这个时候对于应用程序来说,写操作已经完成,至于什么时候在真正写到磁盘是由操作系统决定,除非显示的调用sync同步命令

这里写图片描述

直接I/O的方式

直接IO就是应用程序直接访问磁盘数据,而不经过内核缓冲区,这样做的目的是减少一次从内核缓冲区到用户程序缓存的数据复制。比如说数据库管理系统这类应用,它们更倾向于选择它们自己的缓存机制,因为数据库管理系统往往比操作系统更了解数据库中存放的数据,数据库管理系统可以提供一种更加有效的缓存机制来提高数据库中数据的存取性能。

直接IO的缺点:如果访问的数据不在应用程序缓存中,那么每次数据都会直接从磁盘加载,这种直接加载会非常缓慢。通常直接IO与异步IO结合使用,会得到比较好的性能。
这里写图片描述

同步访问I/O的方式

同步访问的方式就是数据的读取写入操作都是同步的,它与标准访问文件的方式不同的是,只有当数据被写入到磁盘上才会返回给应用程序写入成功.

异步访问I/O的方式

异步访问文件的方式就是当访问数据的线程发出请求之后,线程会继续处理其他工作,而不用阻塞等待,当请求的数据返回之后再处理后面的操作,这种方式可以提供应用程序的效率,但是不会改变对文件的访问效率.

内存映射的方式

内存映射是指将硬盘上文件的位置与进程逻辑地址空间中一块大小相同的区域一一对应,当要访问内存中一段数据时,转换为访问文件的某一段数据。这种方式的目的同样是减少数据在用户空间和内核空间之间的拷贝操作。当大量数据需要传输的时候,采用内存映射方式去访问文件会获得比较好的效率。 使用内存映射文件处理存储于磁盘上的文件时,将不必再对文件执行I/O操作,这意味着在对文件进行处理时将不必再为文件申请并分配缓存,所有的文件缓存操作均由系统直接管理,由于取消了将文件数据加载到内存、数据从内存到文件的回写以及释放内存块等步骤,使得内存映射文件在处理大数据量的文件时能起到相当重要的作用

这里写图片描述

java访问磁盘文件

在Java中 获取一个File代表的是指定路径的一个虚拟对象,这个对象可能是文件,可能是文件夹,甚至可能不存在。
只有在真正使用的时候才会检查这个文件是否存在,例如FileInputStream对象,下面是FileInputStream的源码片段。创建一个FileInputStream对象时,会同时创建一个FileDescriptor对象。FileDescriptor就是真正代表一个文件对象的描述,通过这个对象来于操作系统进行交互。

    public FileInputStream(File file) throws FileNotFoundException {        String name = (file != null ? file.getPath() : null);        SecurityManager security = System.getSecurityManager();        if (security != null) {            security.checkRead(name);        }        if (name == null) {            throw new NullPointerException();        }        if (file.isInvalid()) {            throw new FileNotFoundException("Invalid file path");        }        fd = new FileDescriptor();        fd.incrementAndGetUseCount();        this.path = name;        open(name);    }

Java Socket的工作机制

这里写图片描述

连接建立成功后,服务端与客户端都会有一个Socket实例,每个Socket实例都有一个inputStream和outputStream,并且通过这两个对象来进行数据交互.
操作系统会为inputStream何outputStream分别分配一块缓存区,数据的读取写入都是通过缓存区完成的。
写入端的数据写入到outputStream对应的SendQ队列中,当队列满了,数据发送到另外一端的inputStream的RecvQ队列中。如果RecvQ已经满了,那么outputStream的write方法阻塞,直到RecvQ队列有足够的空间容纳SendQ发送的数据。

原创粉丝点击