Java 之NIO(一) - 简介(Buffer)

简介
NIO是New IO 的简称,在jdk1.4 里提供的新api。
与原始的java io包中面向流(stream-oriented)概念不同,NIO中采用面向块的(block-oriented)概念,这意味着在尽可能的情况下,IO操作以块为单位进行,而不是字节/字符为单位,采用这种方式可以使Java IO性能有大幅提高。

另外,与面向线程的,阻塞式IO方式相比,多道通信,非阻塞式IO机制可以更加有效的处理大量连接的应用程序。

新IO除了原有功能之外,还提供了以下新特性:

1.多路选择的,非阻塞式IO机制

2.支持文件锁及内存映射

3.支持正则表达式的模式匹配设施

4.字符集编码器和译码器

在新IO中通过使用Buffer和Channel支持以上特性。

Buffer(缓冲区)

在NIO中所有操作都需要使用缓冲区处理,所有的读写都是通过缓冲区来完成的,缓冲区是一个线性的,有序的数据集,只能存储基本数据类型。

关于Buffer类的常用方法,请查阅javadoc文档,这里不再一一列出,NIO中针对每一种基本数据类型都有一个相应的缓冲区操作类,包括(ByteBuffer,CharBuffer,ShortBuffer,IntBuffer,LongBuffer,FloatBuffer,DoubleBuffer),关于各缓冲区操作类的方法,请查阅javadoc文档。

1. Buffer的基本用法,代码如下:

package com.ray.nio.demo;
import java.nio.IntBuffer;
/**
 * 缓冲区的基本操作
 * 
 * 在Buffer中存在一系列状态变量,用于记录操作缓冲区内容的指针位置(position),指针的操作边界(limit),缓冲区大小(capacity)
 * position:表示下一个操作(读/写)的指针,指针永远放在最后(读/写)入的数据位置之后
 * limit:limit为定界position指针的操作边界,position<limit
 * capacity:定义缓冲区大小
 * 本示例程序操作步骤:
 * 1.为IntBuffer开辟缓冲区
 * 2.向缓冲区写入数据
 * 3.调用flip方法,重设缓冲区 操作指针(position)、操作边界(limit)的位置
 * 4.遍历缓冲区数据
 * 
 * @author xuleilei
 *
 */
public class IntBuffer01 {
public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(10);

System.out.println("未写入数据时的缓冲区:position:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",capacity:" + intBuffer.capacity());

int pre = 0,arrs[] = {1,3,5};
intBuffer.put(pre);
intBuffer.put(arrs);
// intBuffer.put(6, 9); 此处写入数据无效,确保缓冲区起始数据依次连续存储数据

System.out.println("写入数据后的缓冲区:position:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",capacity:" + intBuffer.capacity());

// 重设缓冲区,将
intBuffer.flip();
System.out.println("调用filp方法后的缓冲区:position:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",capacity:" + intBuffer.capacity());

// 读取缓冲区中的内容
// System.out.print(intBuffer.get(3));
while(intBuffer.hasRemaining()){
System.out.print(intBuffer.get() + "、");
}
}
}

2.创建子缓冲区

package com.ray.nio.demo;
import java.nio.IntBuffer;

/**
 * 创建子缓冲区
 * 
 * NIO中可以使用各缓冲区类的slice方法从一个缓冲区中创建一个子缓冲区,子缓冲区与原缓冲区共享数据
 * 
 * @author xuleilei
 *
 */
public class IntBuffer02 {
public static void main(String[] args) {
// 开辟缓冲区空间,声明子缓冲区
IntBuffer intBuffer = IntBuffer.allocate(10),subBuffer = null;

// 向缓冲区中填充数据,10个偶数
for(int i = 0;i < 10;i++){
intBuffer.put(i*2);
}
// 将缓冲区的position指针设置在第三个元素上,并设置边界为5
intBuffer.position(2);
intBuffer.limit(5);
// 开辟子缓冲区
subBuffer = intBuffer.slice();

// 修改子缓冲区数据
for(int i=0;i<subBuffer.capacity();i++){
subBuffer.put(subBuffer.get(i) - 1);
}
// 重设缓冲区
intBuffer.flip();
intBuffer.limit(intBuffer.capacity());
// 读取缓冲区中的内容
while(intBuffer.hasRemaining()){
System.out.print(intBuffer.get() + "、");
}
}
}

3.创建只读缓冲区

package com.ray.nio.demo;
import java.nio.IntBuffer;

/**
 * 创建只读缓冲区
 * 
 * 如果希望使用缓冲区的内容,但又不希望缓冲区内容被修改,则可以通过asReadOnlyBuffer创建一个只读缓冲区。
 * 注意:创建后的只读缓冲区不能变为可写状态
 * @author xuleilei
 *
 */
public class IntBuffer03 {

public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(10);
IntBuffer intBufferReadOnly = null;

int pre = 0,arrs[] = {1,3,5};
intBuffer.put(pre);
intBuffer.put(arrs);
// 将缓冲区变为只读缓冲区,注意在转换为只读缓冲区之前,原缓冲区确保
intBufferReadOnly = intBuffer.asReadOnlyBuffer();
// 重设缓冲区
intBufferReadOnly.flip();
// 读取缓冲区中的内容
while(intBufferReadOnly.hasRemaining()){
System.out.print(intBufferReadOnly.get() + "、");
}
// 向只读缓冲区中写入数据,非法!!!
intBufferReadOnly.put(222);
}


}

4.创建直接缓冲区

在介绍如何创建直接缓冲区前,先简单介绍下直接缓冲区和非直接缓冲区:
操作系统有用户空间与系统空间的概念,JVM对应的JAVA进程是位于用户空间的,处于该空间的进程不能直接访问硬件设备,当JAVA进程要进行I/O操作时,只能通过系统调用将控制权交给内核,内核准备好进程所需要的数据,将这些数据拷贝到用户空间缓冲区(如下图所示)

Java 之NIO(一) - 简介(Buffer)_第1张图片
字节缓冲区要么是直接的,要么是非直接的,最大的区别是于内存位置不同。直接缓冲区可以通过allocateDirect方法来创建,也可以通过内存映射来创建,如果为直接字节缓冲区JVM会尽最大努力直接在此缓冲区上执行本机 I/O 操作。

非直接缓冲区写入步骤:
1.创建一个临时的直接ByteBuffer对象。
2.将非直接缓冲区的内容复制到临时缓冲中。
3.使用临时缓冲区执行低层次I/O操作。
4.临时缓冲区对象离开作用域,并最终成为被回收的无用数据。
如果采用直接缓冲区会少一次复制过程,如果需要循环使用缓冲区,用直接缓冲区可以很大地提高性能。虽然直接缓冲区使JVM可以进行高效的I/o操作,但它使用的内存是操作系统分配的,绕过了JVM堆栈,建立和销毁比堆栈上的缓冲区要更大的开销

package com.ray.nio.demo;
import java.nio.ByteBuffer;
/**
 * 创建直接缓冲区
 * @author xuleilei
 *
 */
public class IntBuffer04 {
public static void main(String[] args) {
ByteBuffer byteBuf = ByteBuffer.allocateDirect(10);

byte arrs[] = {1,3,5};
byteBuf.put(arrs);
// 重设缓冲区
byteBuf.flip();
// 读取缓冲区中的内容
while(byteBuf.hasRemaining()){
System.out.print(byteBuf.get() + "、");
}
}


}

关于NIO缓冲区的基本概念及使用先讲到这里,关于Channel的讲解,继续关注...

你可能感兴趣的:(Java 之NIO(一) - 简介(Buffer))