您的位置:首页 > Web前端

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

2016-03-23 13:14 190 查看
简介

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操作时,只能通过系统调用将控制权交给内核,内核准备好进程所需要的数据,将这些数据拷贝到用户空间缓冲区(如下图所示)




字节缓冲区要么是直接的,要么是非直接的,最大的区别是于内存位置不同。直接缓冲区可以通过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的讲解,继续关注...
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: