java的nio是水平触发吗?在linux上,其实现是基于linux epoll的。所以首先我们要了解epoll。
epoll 水平触发
在epoll 水平触发与边缘触发一文中讲述了水平触发的条件:
- 对于读操作
只要缓冲内容不为空,LT模式返回读就绪。 - 对于写操作
只要缓冲区还不满,LT模式会返回写就绪。
所以,Linux epoll的水平触发是以缓冲区空满状态来判断的。
那java nio是水平触发吗
首先我们知道了,Linux epoll的水平触发是以缓冲区空满状态来判断的。
所以,验证java nio水平触发的办法是客户端写多个字节(比如1000个),服务端每次都不读取字节,缓冲区一直没读完,处于非空状态。由于水平触发,读事件应当会一直触发。
如果能多次触发读事件,就应当是水平触发,我们用以下代码验证:
在下列代码中:
- 客户端发送"hello world"后即停止运行。
- 服务端会处理连接事件,但对读事件不会做任何处理,使得读取缓冲区始终非空。
服务端:
public class NioServer {
public static void main(String[] argv) throws Exception {
Selector selector = Selector.open();
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress("0.0.0.0", 1339));
server.configureBlocking(false);
server.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
if (n == 0) continue;
System.out.println("Select " + n);
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
if (key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel accept = channel.accept();
accept.configureBlocking(false);
accept.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel channel = (SocketChannel) key.channel();
// 不读取任何数据
}
}
System.out.println("休眠一秒, 减缓输出, 便于观察");
Thread.sleep(1000);
}
}
}
客户端:
public class NioClient
{
private static SocketChannel socketChannel = null;
private static Charset charset = Charset.forName("GBK");
public static void init() throws IOException
{
socketChannel = SocketChannel.open();
InetAddress ia = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(ia, 1339);
socketChannel.connect(isa);
socketChannel.configureBlocking(false);
System.out.println("与服务器的连接建立成功!");
}
public static void main(String[] args) throws InterruptedException, IOException {
init();
socketChannel.write(charset.encode("hello world"));
}
}
输出:
服务端
现象:
- 客户端发送"hello world"后即结束运行。
-
服务端始终没读取字节,缓冲区非空。即使调用了
keys.remove();
删除key,在之后的循环中依旧会一直触发读取事件。
结论
只要缓冲区非空,就能一直触发读取事件。所以linux中,java nio是水平触发的
网友评论