接上一篇《NIO.2特性总结(二)增强的通道 NetworkChannel》
We’ve finally reached the most powerful feature introduced in NIO.2, the asynchronous channel API.
我觉得第一个问题就是同步和异步的区别,The Difference Between Synchronous And Asynchronous。异步IO有时候也会叫overlapped I/O。这里别跟阻塞和非阻塞混淆了。我们这节主要说的是异步的问题,如果你不了解阻塞和非阻塞,你可以看之前的文章,再罗嗦一点,异步和非阻塞不是一个东西。
同步I/O操作:In a synchronous I/O operation, a thread enters into action and waits until the I/O request is completed (the program is “stuck” waiting for the process to end, with no way out)。其实看这段我特别别扭,怎么看怎么像阻塞的解释。不过我在网上查到了这么一段话:阻塞IO,非阻塞IO,IO复用,信号驱动IO,异步IO,前四种都属于同步IO。这么一看好像还是那么一回事儿。具体我还得去核实一下。
异步I/O操作:When the same action occurs in an asynchronous environment, a thread performs the I/O operation with more kernel help. Actually, it immediately passes the request to the kernel and continues on to process another job. The kernel signals to the thread when the operation has completed, and the thread “respects” the signal by interrupting its current job and processing the data from the I/O operation as necessary。这个是不是又容易和Non-blocking混淆了,我的理解,异步操作会将任务直接交给内核,非阻塞虽然也返回,但是始终是在自己的线程上,他们之间的关系还是看下面:
网上也有很多例子,也有不少UML的图来解释这四个词的含义,这个需要大家慢慢去体会,水还是很深的。
Java7中异步IO的实现是建立在两件事情上:
1. 在异步通道上独立执行的线程(读、写、连接、关闭等)操作
2. 在操作初始化之后的控制机制
Java7的异步IO操作有两种form:
1. Pending Result:这个结果返回的是concurrent包中的Future对象。
2. Complete Result:采用CompleteHandler的回调机制返回结果。
我们看异步通道所有的异步通道有需要遵循一个规则,在不阻塞应用去执行其他任务的情况下初始化IO操作和IO结束后的通知机制。在java7中有三个异步通道:AsynchronousFileChannel、AsynchronousSocketChannel和AsynchronousServerSocketChannel。另外还有一个比较重要的概念是group,要有组的概念是为了把资源共享,每一个异步的channel都会属于一个group,同一个group里的对象就可以共享一个线程池。这个group由AsynchronousChannelGroup,实现。
接下来不多写了,直接上代码,还是代码容易理解,更多的代码请看《pro java7 nio.2》,我只是摘了一部分:
异步文件:
package com.a2.nio2.chapter9.aio;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.Future;
public class AIOFileChannal {
public static void main(String[] args) {
ByteBuffer buffer = ByteBuffer.allocate(100);
String encoding = System.getProperty("file.encoding");
Path path = Paths.get("C:/rafaelnadal/grandslam/RolandGarros",
"story.txt");
try (AsynchronousFileChannel asynchronousFileChannel = AsynchronousFileChannel
.open(path, StandardOpenOption.READ)) {
Future<Integer> result = asynchronousFileChannel.read(buffer, 0);
while (!result.isDone()) {
System.out.println("Do something else while reading ...");
}
System.out.println("Read done: " + result.isDone());
System.out.println("Bytes read: " + result.get());
} catch (Exception ex) {
System.err.println(ex);
}
buffer.flip();
System.out.print(Charset.forName(encoding).decode(buffer));
buffer.clear();
}
}
异步socket,服务端:
package com.a2.nio2.chapter9.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
public class EchoServer {
public static void main(String[] args) {
final int DEFAULT_PORT = 5555;
final String IP = "127.0.0.1";
// create an asynchronous server socket channel bound to the default
// group
try (AsynchronousServerSocketChannel asynchronousServerSocketChannel = AsynchronousServerSocketChannel
.open()) {
if (asynchronousServerSocketChannel.isOpen()) {
// set some options
asynchronousServerSocketChannel.setOption(
StandardSocketOptions.SO_RCVBUF, 4 * 1024);
asynchronousServerSocketChannel.setOption(
StandardSocketOptions.SO_REUSEADDR, true);
// bind the asynchronous server socket channel to local address
asynchronousServerSocketChannel.bind(new InetSocketAddress(IP,
DEFAULT_PORT));// display a waiting message while ...
// waiting clients
System.out.println("Waiting for connections ...");
while (true) {
Future<AsynchronousSocketChannel> asynchronousSocketChannelFuture = asynchronousServerSocketChannel
.accept();
try (AsynchronousSocketChannel asynchronousSocketChannel = asynchronousSocketChannelFuture
.get()) {
System.out.println("Incoming connection from: "
+ asynchronousSocketChannel.getRemoteAddress());
final ByteBuffer buffer = ByteBuffer
.allocateDirect(1024);
// transmitting data
while (asynchronousSocketChannel.read(buffer).get() != -1) {
buffer.flip();
asynchronousSocketChannel.write(buffer).get();
if (buffer.hasRemaining()) {
buffer.compact();
} else {
buffer.clear();
}
}
System.out.println(asynchronousSocketChannel
.getRemoteAddress()
+ " was successfully served!");
} catch (IOException | InterruptedException
| ExecutionException ex) {
System.err.println(ex);
}
}
} else {
System.out
.println("The asynchronous server-socket channel cannot be opened!");
}
} catch (IOException ex) {
System.err.println(ex);
}
}
}
客户端:
package com.a2.nio2.chapter9.aio;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Random;
import java.util.concurrent.ExecutionException;
public class EchoClient {
public static void main(String[] args) {
final int DEFAULT_PORT = 5555;
final String IP = "127.0.0.1";
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
ByteBuffer helloBuffer = ByteBuffer.wrap("Hello !".getBytes());
ByteBuffer randomBuffer;
CharBuffer charBuffer;
Charset charset = Charset.defaultCharset();
CharsetDecoder decoder = charset.newDecoder();
// create an asynchronous socket channel bound to the default group
try (AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel
.open()) {
if (asynchronousSocketChannel.isOpen()) {
// set some options
asynchronousSocketChannel.setOption(
StandardSocketOptions.SO_RCVBUF, 128 * 1024);
asynchronousSocketChannel.setOption(
StandardSocketOptions.SO_SNDBUF, 128 * 1024);
asynchronousSocketChannel.setOption(
StandardSocketOptions.SO_KEEPALIVE, true);
// connect this channel's socket
Void connect = asynchronousSocketChannel.connect(
new InetSocketAddress(IP, DEFAULT_PORT)).get();
if (connect == null) {
System.out.println("Local address: "
+ asynchronousSocketChannel.getLocalAddress());
// transmitting data
asynchronousSocketChannel.write(helloBuffer).get();
while (asynchronousSocketChannel.read(buffer).get() != -1) {
buffer.flip();
charBuffer = decoder.decode(buffer);
System.out.println(charBuffer.toString());
if (buffer.hasRemaining()) {
buffer.compact();
} else {
buffer.clear();
}
int r = new Random().nextInt(100);
if (r == 50) {
System.out
.println("50 was generated! Close the asynchronous socket channel!");
break;
} else {
randomBuffer = ByteBuffer.wrap("Random number:"
.concat(String.valueOf(r)).getBytes());
asynchronousSocketChannel.write(randomBuffer).get();
}
}
} else {
System.out.println("The connection cannot be established!");
}
} else {
System.out
.println("The asynchronous socket channel cannot be opened!");
}
} catch (IOException | InterruptedException | ExecutionException ex) {
System.err.println(ex);
}
}
}
-------------------------------------------------------
其他还有比如怎么启多线程去处理请求,怎么操作group,书上都有就不贴出来了。这个还真需要点时间去研究研究。
当然还有个问题就是非阻塞怎么和异步去结合,这个问题我也会抽空去研究一下。
后面的相关文章会补上NIO.2在文件系统操作上的新特性.
来源:oschina
链接:https://my.oschina.net/u/589742/blog/93718