1. IO基本实现
1.1 字节流(stream)
个人理解: 以字节为单位对文件本身直接操作。
主要接口:
InputStream & OutputStream
主要实现:
FileInputStream & FileOutputStream
FilterInputStream & FilterOutputStream
ByteArrayInputStream & ByteArrayOutputStream
1.2 字符流(buffer)
个人理解:以字符为单位利用缓冲区对文件操作,具体读(写)操作会先读(写)缓冲区。
关于字节流和字符流的区别
实际上字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的,但是字符流在操作文件的时候是会用到缓冲区的,是通过缓冲区来操作文件的。
读者可以试着将上面的字节流和字符流的程序的最后一行关闭文件的代码注释掉,然后运行程序看看。你就会发现使用字节流的话,文件中已经存在内容,但是使用字符流的时候,文件中还是没有内容的,这个时候就要刷新缓冲区。
IO缺点
按字节操作,效率不高, 大部分线程都在阻塞,浪费计算资源;
当数据源没有数据,则IO阻塞。
2. NIO (全称java non-blocking IO, 无阻塞IO)
在了解NIO之前,先搞清楚Java程序文件读写和虚拟内存的原理。
Java程序文件读写(IO)
当应用程序需要读取文件的时候,内核首先通过DMA技术将文件内容从磁盘读入内核中的buffer,然后Java应用进程再从内核的buffer将数据读取到应用程序的buffer。
简单点说:先从Disk(硬盘)读到Kernel(内核),再从Kernel(内核)读到进程(Process)的buffer。下图就是IO读工作原理:
为了提升I/O效率和处理能力,操作系统采用虚拟内存的机制。
虚拟内存(交换内存)
虚拟内存实际上是Disk上得文件,主要作用有两个:
-
不同的虚拟内存可以映射到相同的物理内存。
说白了,就是硬盘内不同的文件,可以映射到相同的物理内存地址。
根据这个原理,可以简化文件读取流程,提升读取效率,效果如下图所示:
通过使用虚拟内存技术,将应用程序的buffer和内核的buffer都作为虚拟内存,并且两块不同的虚拟内存指向相同的物理内存,内核通过DMA将数据读取到buffer的时候,应用程序就可以直接使用这些数据了。
简单点说: 先从Disk读到Kernel的虚拟内存(硬盘的文件),因为kernel的虚拟内存和应用程序的虚拟内存映射相同的物理内存,所以应用程序可以直接用了,不用读多一次。 通过使用虚拟内存(Disk的文件), 应用程序可以使用比物理内存所能容纳得大得多的内存,并且也能够提高I/O效率。当物理内存中的数据不使用的时候,可以将物理内存中的数据放到虚拟内存中,操作系统就可以腾出物理内存空间存储新的要处理的数据。当需要使用虚拟内存中的数据时,再可以把虚拟内存中的数据加载到物理内存中。因此物理内存可以看做时虚拟内存中存放数据的cache。而上面所述的cache虚拟内存数据的过程,对应用程序来说时透明的,它可以像处理物理内存数据一样处理虚拟内存中的数据。
简单点说:因为有虚拟内存(缓存),应用程序能够更加快速的读写比物理内存更大的文件。
知道上面虚拟内存原理,继续NIO的核心内容。
Java NIO 主要由以下三个核心部分组成:
Channel 通道
通道是 I/O 传输发生时通过的入口,而缓冲区是这些数 据传输的来源或目标。对于离开缓冲区的传输,您想传递出去的数据被置于一个缓冲区,被传送到通道。对于传回缓冲区的传输,一个通道将数据放置在您所提供的缓冲区中。Buffer 缓冲区
引入了缓冲区的概念,当数据到达时,可以预先被写入缓冲区,再由缓冲区交给线程,因此线程无需阻塞地等待IO。-
Selectors 选择器(监管多个通道)
通道和缓冲区的机制,使得线程无需阻塞地等待IO事件的就绪,但是总是要有人来监管这些IO事件。这个工作就交给了selector来完成,这就是所谓的同步。Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪,这就是所说的轮询。一旦这个方法返回,线程就可以处理这些事件。
Selector中注册的感兴趣事件有:
OP_ACCEPT
OP_CONNECT
OP_READ
OP_WRITE
** NIO主要实现:**
- FileChannel:从文件中读写数据。
- DatagramChannel:能通过UDP读写网络中的数据。
- SocketChannel:能通过TCP读写网络中的数据。
- ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。
NIO2
NIO2是Java7 添加了4个异步通道,分别包括:
- AsynchronousSocketChannel
- AsynchronousServerSocketChannel
- AsynchronousFileChannel
- AsynchronousDatagramChannel
NIO.2 的异步通道 APIs 提供方便的、平台独立的执行异步操作的标准方法。这使得应用程序开发人员能够以更清晰的方式来编写程序,而不必定义自己的 Java 线程,此外,还可通过使用底层 OS 所支持的异步功能来提高性能。如同其他 Java API 一样,API 可利用的 OS 自有异步功能的数量取决于其对该平台的支持程度。
自己理解: NIO2在原来的NIO包加入了异步处理的API。
返回Future结果集,更好控制IO异步处理。
对象序列化
序列化的原理:
Serialization(序列化)是一种将对象以一连串的字节描述的过程。
反序列化的原理:
反序列化deserialization是一种将这些字节重建成一个对象的过程。
序列化的意义:
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。
这就需要有一种可以在两端传输数据的协议。Java序列化机制就是为了解决这个问题而产生。
如何序列化:
一个对象能够序列化的前提是实现Serializable接口,Serializable接口没有方法,更像是个标记。
有了这个标记的Class就能被序列化机制处理。
Java序列化算法
- 将对象实例相关的类元数据输出。
- 递归地输出类的超类描述直到不再有超类。
- 类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。
- 从上至下递归输出实例的数据。
serialVersionUID值的重要作用
根据上面的分析,可以发现如果一个类可序列化,serialVersionUID建议给一个确定的值,不要由系统自动生成,否则在增减字段(不能修改字段类型及长度)时,如果两边的类的版本不同会导致反序列化失败.
参考:
基于java的nio消息实现方式优缺点分析及示例代码说明
java nio原理和它的优点
以上纯属个人理解,有误请指正。谢谢!