第一节 第二节 第三节 第四节 第五节 第六节 第七节
第七节    java.nio包
java 1.4中推出的java.nio(新输入/输出)软件包允许像在其他低级语言——如C语言——中一样进行输入/输出。许多开发者认为这些新功能只能进行非阻塞操作;但是,这些新功能提供许多其他新鲜有趣的特性,它们包括:
你可以记忆地图文件。
你能够直接从硬盘上,而不必一字节一字节地读写数据块。(当你在非阅读期间将数据从缓冲器中提出时,它处理低字节优先问题。)
你可以进行非阻塞异步输入/输出。
你能够锁定整个或部分文件。
常规输入/输出以一种相当冗长而又极为不对称的方式堆栈InputStream、Reader和BufferedReader类中你需要的方法为根据。NIO设计更清晰、简单、高效。如果你愿意,你甚至可以用NIO完成所有的输入/输出。在本文中,我将探讨这些功能并讨论其应用过程。
通信通道
NIO API引入一种称作通道的新型原始输入/输出提取方法。通道表示到实体(如硬件设备、文件、网络套接字或者可以执行一个或多个独特的诸如读或写之类输入/输出操作的程序组件)的开放连接。正如在java.nio.channels.Channel接口中所指定的,通道可以处于打开或关闭状态,并且它们既是可异步关闭的,又是可中断的。
操作的一个线程可以被阻止,而另一个线程能够关闭通道,这是通道的一个突出特点。当通道关闭时,被阻止的线程用一个异常激活,表明通道被关闭。
有几个接口对通道接口进行扩张,每个接口指定一个新的输入/输出操作。以下是这些接口的信息:
ReadableByteChannel接口指定一个将字节从通道读入缓冲器的read()方法。
WritableByteChannel接口指定一个将字节从通道写入缓冲器的write()方法。
ScatteringByteChannel和GatheringByteChannel接口分别扩张ReadableByteChannel和WritableByteChannel接口,采用缓冲器序列而非一个单独缓冲器增加read()和write()方法。
FileChannel类支持从连接到文件的通道读取字节或向其写入字节,以及查询和修改当前的文件位置及将文件调整为指定大小等常见操作。它定义了在整个文件或具体文件区域上获取锁定的方法;这些方法返回FileLock类的实例。最后,它定义了对要写入到存储设备的文件进行强行更新的方法、在文件和其他通道之间高效传输字节的方法,以及将文件区域直接映射到内存中的方法。

配置通道
你可以配置通道进行阻塞或非阻塞操作。在阻塞方式下调用读、写或其他操作时,直到操作完成才能返回。例如,在缓慢的套接字上进行大型写操作可能要用很长时间。在非阻塞模式下,在缓慢的套接字上写入大型缓冲器只是排列数据(可能在一个操作系统缓冲器,或在网卡上的缓冲器中)并立即返回。线程可能转向其他任务,而由操作系统的输入/输出管理器完成工作。
从文件通道中移出,可将我们带到读出及写入套接字的连接通道。你还能够以阻塞或非阻塞方式应用这些通道。在阻塞方式下,它们视客户或服务器而替代连接或接受调用。在非阻塞方式下,就没有对应项。[url=http://www.zdnet.com.cn/i/developer/site/2006/7/39515170/lista.htm]列表A[/url]是一个你将字符串”Hello there”送到1234端口并获得回应的例子。
// Setup
InetSocketAddress socketAddress =
new InetSocketAddress(host, 1234);
Charset charset =
Charset.forName("ISO-8859-1");
CharsetDecoder decoder =
charset.newDecoder();
CharsetEncoder encoder =
charset.newEncoder();
// Allocate buffers
ByteBuffer buffer =
ByteBuffer.allocateDirect(512);
CharBuffer charBuffer =
CharBuffer.allocate(512);
// Connect
channel = SocketChannel.open();
channel.connect(socketAddress);
// Send request
String request = "Hello there!";
channel.write(encoder.encode(CharBuffer.wrap(request)));
// Read response
while ((channel.read(buffer)) != -1) {
buffer.flip();
// Decode buffer
decoder.decode(buffer, charBuffer, false);
// Display
charBuffer.flip();
System.out.println(charBuffer);
buffer.clear();
charBuffer.clear();
}
NIO中的缓冲器
缓冲器类提供一种将一组原始数据元素存储在一个内存容器中的机制。除内容外,缓冲器有一个位置,即下一个读或写元素的目录;以及一个限制,即不应被读或写的第一个元素目录。基本的Buffer类定义了这些属性,以及清除、浏览和倒转的方法;标记当前位置、重设定一个标志位置的方法。每个非布尔原始类型:ByteBuffer、CharBuffer、DoubleBuffer和LongBuffer等都有一个缓冲器类。
这些缓冲器类每个支持一组读写数据的get-和put-方法,每个能够被读或写进/出对应的缓冲器类型的原始类型都有许多get-和put-方法。例如,对ByteBuffer而言,就有getChar()、putChar()、getByte()、putByte()、getShort()、putShort(),以及其他一些方法。
直接ByteBuffer有一个特定的形式,称之为MappedByteBuffer。这个类表示映射到一个文件的一个缓冲器字节。要将文件映射到MappedByteBuffer中,首先必须为文件获得一个通道。建立MappedByteBuffer后,就可以像其他ByteBuffer一样对它进行访问。在这个特例中,它是只读性质的,所以任何向putXxxx()方法中写入信息的尝试都会产生一个NonWritableChannelException异常。
java.nio.charset软件包中包含了在字节与Unicode字符间转换的字符集、解码器与编码器。解码器是一个将特定字符集中的字节转换为字符的引擎;编码器是一个将字符转换为字节的引擎。在列表A中,解码器与编码器用来对闲置的请求与回应进行重新编码。
非阻塞操作
非阻塞操作是新输入/输出API最常见与最广泛应用的功能。为将通道配置为非阻塞方式,必须调用configureBlocking()方法。使用SocketChannel,可用下面的代码来完成:
String host = ...;
InetSocketAddress socketAddress =
?new InetSocketAddress(host, 1234);
channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(socketAddress);
调用connect()方法后,它将立即返回执行情况。然后,你必须指出如何处理这样的通道连接。如果通道是可选项的(即它能够通过Selector类并扩张SelectableChannel类来多元化),你就可以用Selector(通过register()方法)来注册通道,然后签署特殊事件。在指定的操作发生时,它会通知你。这些事件由SelectionKey类的域反映定。可用以下代码来完成:
?Selector selector = Selector.open();
?channel.register(selector,
SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
为查明是否发生了有趣的操作,可在它自己的线程中加入一个while循环。
当你收到通知时,它可能与你感兴趣的选定事件有关。你将通过selectedKeys方法及重复获得现成对象的Set。Set中的元素是一个SelectionKey实例。只要你签署了一个ChannelSocket的OP_CONNECT和OP_READ事件,你就必须检查isConnectable()和isReadable()属性(见[url=http://www.zdnet.com.cn/i/developer/site/2006/7/39515170/listb.htm]列表B[/url])。由于在你处理的时候,这组通道可能发生改变,我们正在用remove()方法消除当前处理的条目。
while (selector.select(500) > 0) {
// Get set of ready objects
Set readyKeys = selector.selectedKeys();
Iterator readyItor = readyKeys.iterator();
// Walk through set
while (readyItor.hasNext()) {
// Get key from set
SelectionKey key =
(SelectionKey)readyItor.next();
// Remove current entry
readyItor.remove();
// Get channel
SocketChannel keyChannel =
(SocketChannel)key.channel();
if (key.isConnectable()) {
} else if (key.isReadable()) {
}
}
}
结论
虽然许多开发者认为新的输入/输出功能只提供非阻塞输入/输出操作,但这种看法并不准确。新的输入/输出功能包含几个新概念(如通道)以及通过缓冲器、字符集和文件映射进行输入/输出功能的新方法。java.nio软件包帮助开发者在数据处理时取得更强的性能及更佳的扩展性。