美文网首页
java的nio是水平触发吗

java的nio是水平触发吗

作者: 不存在的里皮 | 来源:发表于2019-04-26 17:25 被阅读0次

    java的nio是水平触发吗?在linux上,其实现是基于linux epoll的。所以首先我们要了解epoll。

    epoll 水平触发

    epoll 水平触发与边缘触发一文中讲述了水平触发的条件:

    1. 对于读操作
      只要缓冲内容不为空,LT模式返回读就绪。
    2. 对于写操作
      只要缓冲区还不满,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是水平触发的

    相关文章

      网友评论

          本文标题:java的nio是水平触发吗

          本文链接:https://www.haomeiwen.com/subject/zeeqnqtx.html