NIO死循环

关于JAVA NIO死循环的研究

NIO写事件比较特殊,触发的条件取决于写缓冲区是否有空闲,不然会进入死循环。

直接上代码

服务端:

    public static void main(String[] args) {
        try {
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            serverChannel.bind(new InetSocketAddress("127.0.0.1", 9999));
            serverChannel.configureBlocking(false);
            Selector sel = Selector.open();
            serverChannel.register(sel, SelectionKey.OP_ACCEPT);
            while(true){
                sel.select();
                Set set = sel.selectedKeys();
                System.out.println(set.size());
                Iterator iterator = set.iterator();
                while(iterator.hasNext()){
                    SelectionKey key = (SelectionKey)iterator.next();
                    iterator.remove();
                    handler(key);
                }
            }   
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public static void handler(SelectionKey key){

        SocketChannel client;
        Selector sel = key.selector();
        try {

            if(key.isAcceptable()){
                ServerSocketChannel ser = (ServerSocketChannel)key.channel();
                client = ser.accept();
                client.configureBlocking(false);
                client.register(sel,SelectionKey.OP_READ);
            }else if(key.isReadable()){
                SocketChannel se = (SocketChannel)key.channel();
                ByteBuffer ch = ByteBuffer.allocate(1024);
                se.read(ch);
                System.out.println(new String(ch.array()));
                ByteBuffer by = ByteBuffer.wrap(new String("我是服务器").getBytes());
                se.write(by);
                se.register(sel, SelectionKey.OP_WRITE);
            }else if(key.isWritable()){
                SocketChannel ch = (SocketChannel)key.channel();
                ByteBuffer by = ByteBuffer.wrap(new String("我是服务器").getBytes());
                ch.write(by);
            }
        } catch (IOException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }


    }
}

客户端:

    public static void main(String[] args) {
        SocketChannel client;
        try {
            client = SocketChannel.open();
            Selector sel = Selector.open();
            client.configureBlocking(false);
            client.register(sel,SelectionKey.OP_CONNECT);
            //并不正在建立连接,直到调用client.finishConnect();
            client.connect(new InetSocketAddress("127.0.0.1", 9999));
            while(true){
                sel.select();
                Iterator it = sel.selectedKeys().iterator();
                while(it.hasNext()){
                    SelectionKey key = (SelectionKey)it.next();
                    handler(key);
                    it.remove();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
    public static void handler(SelectionKey key){
        if(key.isConnectable()){
            SocketChannel client = (SocketChannel)key.channel();
            Selector sel = key.selector();
            //连接就绪
            if(client.isConnectionPending()){
                try {
                    //真正建立连接
                    client.finishConnect();
                    client.configureBlocking(false);//非阻塞
                    client.write(ByteBuffer.wrap(new String("我是客户端").getBytes()));
                    client.register(sel, SelectionKey.OP_READ);
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
            //读就绪
        }else if(key.isReadable()){
            read(key);
        }
    }
    public static void read(SelectionKey key){
        SocketChannel cl = (SocketChannel)key.channel();
        ByteBuffer by = ByteBuffer.allocate(1024);
        try {
            cl.read(by);
            System.out.println(new String(by.array()));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

上诉代码会进入死循环,原因是写事件key.isWritable()

写事件相对读事件比较特殊,一般来说,你最好不要注册写事件。写事件的就绪条件为底层缓冲区有空闲空间,而写缓冲区绝大部分时间都是有空闲空间的,所以当你注册写事件后,写操作一直是就绪的,selector会一直搜索到该事件(写操作),从而一直占用整个CPU资源,进入死循环。所以,只有当你确实有数据要写时再注册写操作,并在写完以后马上取消注册。一般不建议注册写事件。

写博客事件不长,有什么不正确请及时@我,谢谢

你可能感兴趣的:(Java)