2016-12-27 42 views
-1

代码用于使用单个线程Java I /回声服务器O代表使用NIOJAVA NIO是否因持续循环而浪费CPU周期?

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主线程相同

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(); 
    } 

} 

代码来等待在该插座.accept()调用。但是NIO不这样做,因为socketChannel.accept()不是阻塞调用。

于是惯于NIO的程序是连续运行的循环?并导致CPU周期浪费?我能否以更好的方式编写程序?对不起,我对JAVA NIO和异步编程非常陌生。

+0

的Java NIO不循环不断。你的代码可能会,但这会是你的代码浪费CPU周期,而不是Java NIO。注意这里没有什么[标签:异步]。 – EJP

回答

3

在正常IO线程被阻塞上serverSocket.accept()

随着NIO线程被阻塞上selector.select()

Selector#select()的JavaDoc:

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


为什么这个名为 “非阻塞IO”?

其实,你的第一个例子(与普通的IO)有两个阻塞调用:server.accept()in.readLine()

现在考虑有行为不当的当事人的案件:它打开到服务器的连接,但从来没有发送任何数据。对于正常的IO,服务器线程在in.readLine()中等待数据到达,并且直到第一个客户端关闭其连接时才能服务任何其他客户端。

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

所以术语“非阻塞IO”并不意味着服务器线程永远不会堵塞 - 这意味着非运行得客户端无法阻止服务器线程永远。

+0

谢谢托马斯。不知道Selector#select是阻塞呼叫。那么为什么它被称为非阻塞IO,当它的一个API实际上阻塞了-_-对我的无知感到抱歉。 – Ajay

+1

@Ajay如果添加一些更多的解释性文字。我希望能更清楚地表明关于非阻塞IO的观点 - 否则不要犹豫,再问一次 –

+0

非常感谢Thomas的解释。我现在清楚地了解它。感谢您花时间在我的查询上。 – Ajay