异步I/O
JSR 51引入了多路复用I/O(无阻塞I/O和选择就绪的结合)使创建高可扩展服务器变得更加容易,本质上是这样的,客户端代码用一个选择器注册一个套接字通道,当通道准备好可以开始I/O操作时发出通知。
如果要深入研究多路复用I/O,请阅读Ron Hitchens的《Java NIO》一书。
JSR 203还引入了异步I/O,它也被用来建立高可扩展服务器,和多路复用I/O不同,异步I/O是让客户端启动一个I/O操作,当操作完成后向客户端发送一个通知。
异步I/O是通过以下位于java.nio.channels包中的接口和类实现的,它们的名称前面都加了Asynchronous前缀:
· AsynchronousChannel – 标识一个支持异步I/O的通道。
· AsynchronousByteChannel – 标识一个支持读写字节的异步通道,这个接口扩展了AsynchronousChannel。
· AsynchronousDatagramChannel – 标识一个面向数据报套接字异步通道,这个类实现了AsynchronousByteChannel。
· AsynchronousFileChannel – 标识一个可读,写和操作文件的异步通道,这个类实现了AsynchronousChannel。
· AsynchronousServerSocketChannel – 标识一个面向流监听套接字的异步通道,这个类实现了AsynchronousChannel。
· AsynchronousSocketChannel – 标识一个面向流连接套接字的异步通道,这个类实现了AsynchronousByteChannel。
· AsynchronousChannelGroup – 标识一个用于资源共享的异步通道组。
AsynchronousChannel文档指定了两种形式的异步I/O操作:
· Future
· void operation(... A attachment, CompletionHandler handler)
operation列举I/O操作(如读,写),V是操作的结果类型,A是附加给操作的对象类型。
第一种形式需要你调用java.util.concurrent.Future方法检查操作是否完成,等待完成和检索结果,清单2的代码演示了这样一个示例。
清单2. AFCDemo1.java
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.Future;
public class AFCDemo1
{
public static void main (String [] args) throws Exception
{
if (args.length != 1)
{
System.err.println ("usage: java AFCDemo1 path");
return;
}
Path path = Paths.get (args [0]);
AsynchronousFileChannel ch = AsynchronousFileChannel.open (path);
ByteBuffer buf = ByteBuffer.allocate (1024);
Future<Integer> result = ch.read (buf, 0);
while (!result.isDone ())
{
System.out.println ("Sleeping...");
Thread.sleep (500);
}
System.out.println ("Finished = "+result.isDone ());
System.out.println ("Bytes read = "+result.get ());
ch.close ();
}
}
调用AsynchronousFileChannel's public static AsynchronousFileChannel open(Path file, OpenOption... options)方法打开file参数进行读取,然后创建了一个字节缓冲区存储读取操作的结果。
接下来调用public abstract Future
调用read()方法后,进入一个表决循环,重复调用Future的isDone()方法检查操作是否完成,一直等到读操作结束,最后调用Future的get()方法返回读取到的字节大小。
第二种形式需要你指定java.nio.channels.CompletionHandler,并实现下面的方法使用前面操作返回的结果,或是了解操作为什么失败,并采取适当的行动:
· 当操作完成时调用void completed(V result, A attachment),这个操作的结果是由result标识的,附加给操作的对象是由attachment标识的。
· 当操作失败时调用void failed(Throwable exc, A attachment),操作失败的原因是由exc标识的,附加给操作的对象是由attachment标识的。