JAVA NIO是否通过连续循环浪费CPU周期?

时间:2016-12-27 16:32:17

标签: java multithreading nio

使用单线程java I / O的回显服务器代码

public static void main(String[] args) throws Exception {

    // create socket
    int port = 4444;
    ServerSocket serverSocket = new ServerSocket(port);
    System.err.println("Started server on port " + port);

    try {

        // repeatedly wait for connections, and process
        while (true) {

            // a "blocking" call which waits until a connection is requested
            Socket clientSocket = serverSocket.accept();
            System.err.println("Accepted connection from client");

            // open up IO streams
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            PrintStream out = new PrintStream(clientSocket.getOutputStream());

            // waits for data and reads it in until connection dies
            // readLine() blocks until the server receives a new line from client
            String s;
            while ((s = in.readLine()) != null) {
                out.print(s);
            }

            // close IO streams, then socket
            System.err.println("Closing connection with client");
            out.close();
            in.close();
            clientSocket.close();
        }
    } finally {
        serverSocket.close();
    }

}

使用NIO的代码

public static void main(String[] args) throws IOException {
    ServerSocketChannel server = ServerSocketChannel.open();
    server.socket().bind(new InetSocketAddress(PORT_NUMBER));
    server.socket().setReuseAddress(true);
    server.configureBlocking(false);

    Selector selector = Selector.open();
    server.register(selector, SelectionKey.OP_ACCEPT);

    ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
    while (true) {
        int channelCount = selector.select();
        if (channelCount > 0) {
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = keys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    SocketChannel client = server.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ, client.socket().getPort());
                } else if (key.isReadable()) {
                    SocketChannel client = (SocketChannel) key.channel();
                    System.out.println("port: " + key.attachment());
                    if (client.read(buffer) < 0) {
                        key.cancel();
                        client.close();
                    } else {
                        buffer.flip(); // read from the buffer
                          /*
                           * byte[] received = new byte[buffer.remaining()];
                           * buffer.get(received); buffer.clear(); // write into the buffer
                           * buffer.put(received); buffer.flip(); // read from the buffer
                           */
                        client.write(buffer);
                        buffer.clear(); // write into the buffer
                    }
                }
            }
        }
    }
}

这里使用普通的I / O主线程来到socket.accept()调用等待。但是NIO不这样做,因为socketChannel.accept()不是阻塞调用。

那么NIO程序不会持续运行循环吗?并导致浪费CPU周期?我能以更好的方式编写程序吗?对不起,我是JAVA NIO和异步编程的新手。

1 个答案:

答案 0 :(得分:3)

在正常IO中,线程在serverSocket.accept()上被阻止。

使用NIO,线程在selector.select()上被阻止。

来自Selector#select()的JavaDoc:

  

此方法执行阻止选择操作。

为什么这称为“非阻塞IO”?

实际上,您的第一个示例(普通IO)有两个阻止调用:server.accept()in.readLine()

现在考虑一个行为不端的客户端:它打开与服务器的连接,但从不发送任何数据。使用正常IO时,服务器线程会在in.readLine()中等待数据到达,并且在第一个客户端关闭其连接之前无法为任何其他客户端提供服务。

对于NIO,情况有所不同:如果客户端打开连接,服务器线程会唤醒,server.accept()连接并使用相同的选择器注册SocketChannel。然后,服务器线程再次通过selector.select()等待选择器。现在有两种方法可以唤醒服务器线程:另一个客户端连接,或者第一个客户端发送一些数据。

因此术语“非阻塞IO”并不意味着服务器线程永远不会被阻塞 - 这意味着非行为客户端无法永久阻止服务器线程。