《深入分析JavaWEB 技术内幕》第二章深入分析Java I/O的工作机制(上)

基本架构

  • 基于字节操作的I/O接口:InputStream和OutputStream
  • 基于字符操作的I/O接口:Writer和Reader(提供字符流的目的是为了方便字符的处理,无需额外转换编码格式)
  • 基于磁盘操作的I/O接口:File(类似于SQL当中的DDL语句)
  • 基于网络操作的I/O接口:Socket

前两组主要是传输数据的数据格式,后两组主要是传输数据的方式。
字节流和字符流直接可以相互转换,InputStreamReader就能将字节流转换为字符流,OutputStreamWriter能将字符流转换为字节流。
其中还包含节点流和处理流,节点流就是直接的裸的获取或者写入某些资源,而处理流就是包装在节点流外面来加快传输,由于磁盘IO是很耗时的,直接一段一段的写肯定没有一次性写入很多来得快。
InputStream的类结构图大概如下所示(版本Jdk11)
在这里插入图片描述
OutputStream的类结构图大概如下所示(版本Jdk11)
在这里插入图片描述
字符写入操作Writer
在这里插入图片描述
读字符Reader在这里插入图片描述

磁盘I/O工作机制

由于磁盘设备是由操作系统管理的,所以需要调用读写,只能通过通过系统调用的方式来工作,而系统调用可能就会导致内核空间地址和用户空间地址的切换问题(Java多线程也会出现内核态和用户态切换的,所以也存在这种问题),操作系统为了保护系统本身的运行安全,将内核程序运行使用的内存空间和用户程序运行的内存空间隔离开,这样就会存在数据在两个空间之间复制的问题。
可使用缓存来减少直接读取I/O的次数(BufferedInputStream等)

标准访问读取方式

标准访问文件的方式就是应用程序调用read()接口,操作系统首先检查内核的高速缓存中有没有需要的数据,如果有,从缓存中返回,如果没有从磁盘中读取缓存于缓存中。

写入则使用write()接口,将数据从用户地址空间复制到内核地址空间的缓存中。具体写入时间由系统决定,除非写入sync同步命令。

在这里插入图片描述

直接访问方式

直接访问磁盘,不进过内核缓存区。
由应用程序自己缓存所需数据(比如热点数据),还能对数据实现预加载,一般用于数据库管理系统中,
但是每次新数据都需要在磁盘中读取,所以相对耗时。
异步I/O和直接I/O结合使用一般能达到比较好的效果。
在这里插入图片描述

同步访问文件的方式

与标准方式类似,不过只有当数据成功写入后才会返回成功的标志(参考volatile关键字),
性能较差,只有对安全性要求较高时才使用。

异步访问文件的方式

就是不用强调同步,无需阻塞等待结果。

内存映射的方式

内存中的一块区域与磁盘中的文件关联起来,减少数据从内核缓存到倥偬空间缓存的数据复制的操作。
在这里插入图片描述

Java访问磁盘文件

一般的步骤:
1.建立一个File对象(及时直接创建节点流,也是在内部创建一个FIle对象)
2.将FIle与节点流绑定(这一步会检查是否文件真的存在,打开对应文件流,如果是输出,则会创建一个,如果是输入便会抛出异常;然后与FileDescription对象绑定,这对象便能与系统底层交互)
3.用处理流包装节点流
4.操作

Java序列化技术

将对象转换为一串二进制表示的字节数组。
总结一些复杂操作的情况:

  • 父类实现Serializable接口时,子类可以被序列化
  • 子类实现而父类未实现,父类字段会丢失
  • 某类的字段为引用类型时,而该类型未实现Serializable接口,报错
  • 反序列化时,被修改的部分字段会丢失
  • 反序列化时,如果serialVersionUID被修改,失败。

纯Java环境(反)序列化能很好的工作,但是多语言场景下,其他语言便很难还原,建议使用JSON。

网络I/O工作机制

TCP状态

在这里插入图片描述

影响网络传输的因素

  • 网络带宽:一条物理链路在1s内能够传输的最大比特数
  • 传输距离:光速也是有上限的,而且存在一个折射率
  • TCP拥堵控制:带宽×RTT = 缓冲窗口的大小

Java Socket的工作机制

通过ip找到主机位置,通过端口号确认是哪个程序

建立通信链路

操作系统会为创建的Socket实例分配IP与端口号,并创建一个包含本地地址,目标地址和端口的套接字数据结构,直到关闭前该结构都会保留。在构造函数正确返回前,会进行三次握手,完成则创建完成。
ServerSocket则是对应服务器端的,只需使用一个未使用的端口号进行监听即可。

数据传输

每个Socket实例都有对应的输入输出流,通过它们来交换数据。
当创建Socket对象时,操作系统会为其分配一定大小的缓存区,数据的读入与输出都是依靠缓存区完成的,写入端将数据写到OutputStream对应的SendQ队列中,当队列满时,数据被转移到对方的InputStream的RecvQ队列中,如果也满了,write方法就会被阻塞,直到RecvQ有足够的空间容纳SendQ的数据为止。
如果两边同时发数据,阔能出现死锁的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值