BIO 和 NIO 做爲 Server 端,當創建了 10 個鏈接時,分別產生多少個線程?html
答案: 由於傳統的 IO 也就是 BIO 是同步線程堵塞的,因此每一個鏈接都要分配一個專用線程來處理請求,這樣 10 個鏈接就會建立 10 個線程去處理。而 NIO 是一種同步非阻塞的 I/O 模型,它的核心技術是多路複用,可使用一個連接上的不一樣通道來處理不一樣的請求,因此即便有 10 個鏈接,對於 NIO 來講,開啓 1 個線程就夠了。java
public class DemoServer extends Thread { private ServerSocket serverSocket; public int getPort() { return serverSocket.getLocalPort(); } public void run() { try { serverSocket = new ServerSocket(0); while (true) { Socket socket = serverSocket.accept(); RequestHandler requestHandler = new RequestHandler(socket); requestHandler.start(); } } catch (IOException e) { e.printStackTrace(); } finally { if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws IOException { DemoServer server = new DemoServer(); server.start(); try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) { BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream())); bufferedReader.lines().forEach(s -> System.out.println(s)); } } } // 簡化實現,不作讀取,直接發送字符串 class RequestHandler extends Thread { private Socket socket; RequestHandler(Socket socket) { this.socket = socket; } @Override public void run() { try (PrintWriter out = new PrintWriter(socket.getOutputStream());) { out.println("Hello world!"); out.flush(); } catch (Exception e) { e.printStackTrace(); } } }
這樣,一個簡單的 Socket 服務器就被實現出來了。面試
(圖片來源於楊曉峯)服務器
public class NIOServer extends Thread { public void run() { try (Selector selector = Selector.open(); ServerSocketChannel serverSocket = ServerSocketChannel.open();) {// 建立 Selector 和 Channel serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888)); serverSocket.configureBlocking(false); // 註冊到 Selector,並說明關注點 serverSocket.register(selector, SelectionKey.OP_ACCEPT); while (true) { selector.select();// 阻塞等待就緒的 Channel,這是關鍵點之一 Set<SelectionKey> selectedKeys = selector.selectedKeys(); Iterator<SelectionKey> iter = selectedKeys.iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); // 生產系統中通常會額外進行就緒狀態檢查 sayHelloWorld((ServerSocketChannel) key.channel()); iter.remove(); } } } catch (IOException e) { e.printStackTrace(); } } private void sayHelloWorld(ServerSocketChannel server) throws IOException { try (SocketChannel client = server.accept();) { client.write(Charset.defaultCharset().encode("Hello world!")); } } // 省略了與前面相似的 main }
能夠看到,在前面兩個樣例中,IO 都是同步阻塞模式,因此須要多線程以實現多任務處理。而 NIO 則是利用了單線程輪詢事件的機制,經過高效地定位就緒的 Channel,來決定作什麼,僅僅 select 階段是阻塞的,能夠有效避免大量客戶端鏈接時,頻繁線程切換帶來的問題,應用的擴展能力有了很是大的提升。下面這張圖對這種實現思路進行了形象地說明。微信
(圖片來源於楊曉峯)多線程
Java核心36講socket
近期熱門文章ide
若是你喜歡本文,掃描二維碼關注微信公衆號「王磊的博客」spa