IO指Blocking IO
NIO指new/non-blocking IO
Blocking IO中,对于每一个连接,创建一个线程处理这个连接的IO事件,连接和处理线程之间有1:1的关系。连接数受到JVM线程数的限制。JVM最大线程数限制参考http://jzhihui.iteye.com/blog/1271122,最大线程数由操作系统支持线程数,java初始堆大小,最大堆大小以及每个线程栈大小决定。
NIO使用单个selector可处理多个连接。
IO核心代码
final Socket clientSocket = socket.accept();
new Thread(new Runnable() {
@Override
public void run() {
...
}
}).start();
服务端socket.accept()阻塞直至接受新的connection。
接受新的connection之后,建立新的线程,处理这个连接,每一个连接都有一个线程处理,连接数和线程数是1:1的关系。客户端并发的线程数由同时存活线程数决定。
NIO的基础概念
NIO使用selector-based方法来处理网络数据和事件(The NIO API uses a selector-based approach to handle network events and data)
ByteBuffer:数据容器(不影响原理的理解,之后再讲)。
NIO Selectors:Selector is a NIO component that determines if one or more channels are ready for reading and/or。
选择器:选择器是一个组件,这个组件可以决定此时是否有一个或多个的channels准备好进行读写。由于Selector可以处理多个连接,减轻了IO中的线程负担。
Channel: A channel represents a connection to an entity capable of performing IO operations such as a file or socket.
通道:通道是指连向可进行IO操作实体的连接。通道本质上是特殊的连接,特殊在其连接的对象都可进行IO操作。NIO的通道似乎都是双向的。
Selector的使用,需要5步
创建一个Selector,使得其它的Channel可以注册到Selector
在注册Channel时,要说明Selector对此Channel感兴趣的时间
OP_ACCEPT:socket-accept operations的操作位
OP_CONNECT:socket-connect
OP_READ:read operation
OP_WRITE:write operation
调用Selector.select()阻塞,指导上述事件发生
事件发生之后,可以获取到SelectionKey实例,每个实例都包括Channel的引用和实际发生的事件。
此版本NIO核心代码
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
try {
selector.select();
} catch (IOException ex) {
ex.printStackTrace();
// handle in a proper way
break;
}
Set readyKeys = selector.selectedKeys();
Iterator iterator = readyKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = (SelectionKey) iterator.next();
iterator.remove();
try {
if (key.isAcceptable()) {
ServerSocketChannel server = (ServerSocketChannel)
key.channel();
SocketChannel client = server.accept();
System.out.println("Accepted connection from " +
client);
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE |
SelectionKey.OP_READ, ByteBuffer.allocate(100));
}
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
client.read(output);
}
if (key.isWritable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer output = (ByteBuffer) key.attachment();
output.flip();
client.write(output);
output.compact();
}
} catch (IOException ex) {
key.cancel();
try {
key.channel().close();
} catch (IOException cex) {
}
}
}
从代码中可以看出此版本NIO需要开发者检查网络事件发生,同时触发相应的处理逻辑。
NIO2
NIO允许分发IO操作,当IO操作完成时使用completion handler处理IO事件。completion handler的执行完全由底层系统决定,对开发者隐藏。同时保证了每个channel同时
只有一个completion handler在执行。
NIO2核心代码
channel.read(buffer, buffer,new EchoCompletionHandler(channel));
当读取的IO完成之后,自动执行Echo CompletionHandler的操作。
private final class EchoCompletionHandler implements
CompletionHandler {}
上述内容讲了
1. IO和NIO的本质区别:IO的thread-per-connection的本质以及NIO selector-based approch to handle network data and event.
2. IO实现,NIO1实现以及NIO2实现方式的区别,IO使用accept()阻塞等待连接发生之后,创建新的线程处理此连接;NIO1使用selector对channel进行监听,使用selector.select()阻塞直到某个channel准备好读或写,需要开发者检测事件的发生并提供相应的处理逻辑。NIO1是异步的,selector可监听多个channel,这本质上不同于IO;NIO2同样是基于selector但是NIO2的好处在于允许分发IO,并且提供了complete handler用于在IO完成时,自动执行,这种方法不需要检测事件是否发生,然后使用相应的处理逻辑,这一切由底层实现。
参考:《netty in action》
网友评论