首页 > 技术文章 > JAVA NIO 文章

scugxl 2014-07-31 21:44 原文

http://ifeve.com/java-nio-all/


1.Java NIO系列教程(一) Java NIO 概述

[1]channels Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中

[2]Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便

要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。

2.Java NIO系列教程(二) Channel

FileChannel:

JDK doc:用于读取、写入、映射和操作文件的通道。

文件通道在其文件中有一个当前 position,可对其进行查询修改。该文件本身包含一个可读写的长度可变的字节序列,并且可以查询该文件的当前大小。写入的字节超出文件的当前大小时,则增加文件的大小;截取 该文件时,则减小文件的大小。文件可能还有某个相关联的元数据,如访问权限、内容类型和最后的修改时间;此类未定义访问元数据的方法。

除了字节通道中常见的读取、写入和关闭操作外,此类还定义了下列特定于文件的操作:

  • 以不影响通道当前位置的方式,对文件中绝对位置的字节进行读取写入

  • 将文件中的某个区域直接映射到内存中;对于较大的文件,这通常比调用普通的readwrite 方法更为高效。

  • 强制对底层存储设备进行文件的更新,确保在系统崩溃时不丢失数据。

  • 以一种可被很多操作系统优化为直接向文件系统缓存发送或从中读取的高速传输方法,将字节从文件传输到某个其他通道中,反之亦然

  • 可以锁定某个文件区域,以阻止其他程序对其进行访问。

多个并发线程可安全地使用文件通道。可随时调用关闭方法,正如Channel 接口中所指定的。对于涉及通道位置或者可以更改其文件大小的操作,在任意给定时间只能进行一个这样的操作;如果尝试在第一个操作仍在进行时发起第二个操作,则会导致在第一个操作完成之前阻塞第二个操作。可以并发处理其他操作,特别是那些采用显式位置的操作;但是否并发处理则取决于基础实现,因此是未指定的。

确保此类的实例所提供的文件视图与同一程序中其他实例所提供的相同文件视图是一致的。但是,此类的实例所提供的视图不一定与其他并发运行的程序所看到的视图一致,这取决于底层操作系统所执行的缓冲策略和各种网络文件系统协议所引入的延迟。不管其他程序是以何种语言编写的,而且也不管是运行在相同机器还是不同机器上都是如此。此种不一致的确切性质取决于系统,因此是未指定的。

此类没有定义打开现有文件或创建新文件的方法,以后的版本中可能添加这些方法。在此版本中,可从现有的 FileInputStreamFileOutputStreamRandomAccessFile 对象获得文件通道,方法是调用该对象的 getChannel 方法,这会返回一个连接到相同底层文件的文件通道。

文件通道的状态与其 getChannel 返回该通道的对象密切相关。显式或者通过读取或写入字节来更改通道的位置将更改发起对象的文件位置,反之亦然。通过文件通道更改此文件的长度将更改通过发起对象看到的长度,反之亦然。通过写入字节更改此文件的内容将更改发起对象所看到的内容,反之亦然。

此类在各种情况下指定要求“允许读取操作”、“允许写入操作”或“允许读取和写入操作”的某个实例。通过FileInputStream 实例的getChannel 方法所获得的通道将允许进行读取操作。通过 FileOutputStream 实例的 getChannel 方法所获得的通道将允许进行写入操作。最后,如果使用模式"r" 创建 RandomAccessFile 实例,则通过该实例的getChannel 方法所获得的通道将允许进行读取操作,如果使用模式 "rw" 创建实例,则获得的通道将允许进行读取和写入操作。

如果从文件输出流中获得了允许进行写入操作的文件通道,并且该输出流是通过调用FileOutputStream(File,boolean) 构造方法且为第二个参数传入true 来创建的,则该文件通道可能处于添加模式。在此模式中,每次调用相关的写入操作都会首先将位置移到文件的末尾,然后再写入请求的数据。在单个原子操作中是否移动位置和写入数据是与系统相关的,因此是未指定的。  


import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.nio.channels.FileChannel;


public class NIOStudy {
	public static void main(String[] args) throws Exception{
		//LongBuffer b = null;
		RandomAccessFile f = new RandomAccessFile("a.txt", "rw");
		FileChannel ch = f.getChannel();
		ByteBuffer buf = ByteBuffer.allocate(48);
		int read = ch.read(buf);
		while(read!=-1)
		{
			System.out.println("Read:" + read);
			buf.flip();
			while(buf.hasRemaining())
			{
				System.out.print((char)buf.get());
			}
			buf.clear();
			read =ch.read(buf);
		}
		f.close();
	}
}

Q1:getChannel 实现方式。

Q2:buf.flip()  和 hasRemainging


public final Buffer flip()
反转此缓冲区。首先将限制设置为当前位置,然后将位置设置为 0。如果已定义了标记,则丢弃该标记。

在一系列通道读取或放置 操作之后,调用此方法为一系列通道写入或相对获取 操作做好准备。例如:

 buf.put(magic);    // Prepend header
 in.read(buf);      // Read data into rest of buffer
 buf.flip();        // Flip buffer
 out.write(buf);    // Write header + data to channel

当将数据从一个地方传输到另一个地方时,经常将此方法与 compact 方法一起使用。

返回:
此缓冲区

3.Java NIO系列教程(三) Buffer


推荐阅读