NIO 和 IO 的区别
IO 是以流的方式进行数据传输,它是同步阻塞类型。我们可以将流看成生活中的水流,它是单向的。
NIO 是以通道的方式进行数据传输,它是同步非阻塞类型。我们可以将通道看成铁道,通道是不存储数据的,它采用buffer来存储数据,由通道运输到两端。它是一个双向的。
缓冲区的数据存取
基础类型中,除了boolean没有buffer以外,NIO给每个类型都提供了相应的buffer.
java.nio.Buffer;
|– ByteBuffer
|– CharBuffer
|– IntBuffer
|– ShortBuffer
其中,buffer 提供了一些方法,用于存储和输出数据,以ByteBuffer为例:
1 | //1. 实例化一个buffer,大小为1024字节 |
四个核心属性:
- capacity:容量,表示缓冲区中最大存储数据容量,一但声明,不允许改变。
- limit:界限,表示缓冲区中可操作数据大小。(limit 后数据不能进行读写)
- position:位置,表示缓冲区中正在操作数据的位置。
- positon <= limit <= capacity
直接缓冲区和非直接缓冲区
- 非直接缓冲区:通过allocate() 方法分配缓冲区,在JVM内存中
- 直接缓冲区:通过 allocateDirect() 分配直接缓冲区。建立在物理内存中
管道channel
用于源节点和目标节点的连接。在java nio 中负责缓冲区中的数据传输。类似于流,但是不能访问数据,只能与Buffer进行交互。
DMA:直接存储器
最开始,由CPU进行IO接口请求处理,启用多个线程。如果没有数据,则该线程则进入等待状态。后又采用DMA总线方式控制线程。但DMA还是需要向CPU请求资源,后将DMA替换成Channel。一个完全独立的处理器,用来处理IO.无需向CPU申请。
- Java.nio.channels.channel 接口:
- FileChannel
- SocketChannel
- ServerSocketChannel
- DatagranChannel
- 获取通道 getChannel
- 本地IO
- FileInputStream / FileOutputStream
- RandomAccessFile
- 网络IO
- Socket
- ServerSocket
- DatagramSocket
- JDK 1.7
- open
- Files工具类的newByteChannel()
- 本地IO
通道数据传输和内存映射文件
- 以下是四大文件操作对比:
- 通道(Channel)的数据传输(采用非直接缓冲区)
1 |
|
- 内存映射文件(采用直接缓冲区)
1 |
|
- transferTo&transferFrom将数据从源通道传输到其他 Channel 中(采用直接缓存区)
1 | FileChannel inChannel = FileChannel.open(Paths.get("D:\\nio.zip"), StandardOpenOption.READ); |
分散读取和聚集写入
分散读取:将通道中的数据分散到多个缓冲区中
1 | //分配制定大小缓冲区 |
聚集写入:将多个缓冲区中的数据集中到通道中1
2
3
4
5
6
7
8
9//写入完毕之后 for 循环
for(ByteBuffer byteBuffer : buffers) {
//切换成读的模式
byteBuffer.flip();
}
RandomAccessFile randomAccessFile = new RandomAccessFile("test2.txt", "rw");
//获取通道
FileChannel channel2 = randomAccessFile.getChannel();
channel2.write(buffers);
字符集Charset
在java.nio.charset包中共提供了Charset。向ByteBuffer中存放数据时需要考虑字符集的编码方式,从中读取时需要考虑字符集的解码。要读和写文本需要分别使用CharsetDecoder(解码器)和CharsetEncoder(编码器)。
在JDK源码中提供如下静态方法得到一个CharSet实例:
1 | CharSet cs = CharSet.forName(“编码方式”); |
得到一个CharSet实例后,我们需要创建一个编码器和一个解码器,使用下面方法进行创建:
1 | CharSetDecoder decoder = cs.newDecoder(); |
接着我们把ByteBuffer传递给decoder进行编码,返回一个CharBuffer:
1 | CharBuffer cb = decoder.decode(inputData); |
然后我们可以使用encoder进行解码返回一个ByteBuffer:
1 | ByteBuffer outputData = encoder.encode(cb); |
阻塞模式和非阻塞模式
阻塞模式:
- 将直接使用accept()监听事件
非阻塞模式:
- 采用选择器,将通道注册到选择器上, 并指定监听接收事件
1 | /** client */ |
管道(pipe)
Java NIO 管道式两个线程之间的单向数据连接。
Pipe 有一个source通道和一个sink通道,数据会被写到sink通道,从source通道读取。
1 | //1.获取管道 |