Java NIO 簡介
JAVA NIO有兩種解釋:一種叫非阻塞IO(Non-blocking I/O),另外一種也叫新的IO(New I/O),實際上是同一個概念。它是一種同步非阻塞的I/O模型,也是I/O多路複用的基礎,已經被愈來愈多地應用到大型應用服務器,成爲解決高併發與大量鏈接、I/O處理問題的有效方式。java
NIO是一種基於通道和緩衝區的I/O方式,它可使用Native函數庫直接分配堆外內存(區別於JVM的運行時數據區),而後經過一個存儲在java堆裏面的DirectByteBuffer對象做爲這塊內存的直接引用進行操做。這樣能在一些場景顯著提升性能,由於避免了在Java堆和Native堆中來回複製數據。服務器
Java NIO組件
NIO主要有三大核心部分:Channel(通道),Buffer(緩衝區), Selector(選擇器)。傳統IO是基於字節流和字符流進行操做(基於流),而NIO基於Channel和Buffer(緩衝區)進行操做,數據老是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。Selector(選擇區)用於監聽多個通道的事件(好比:鏈接打開,數據到達)。所以,單個線程能夠監聽多個數據通道。網絡
Buffer
Buffer(緩衝區)是一個用於存儲特定基本類型數據的容器。除了boolean外,其他每種基本類型都有一個對應的buffer類。Buffer類的子類有ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer 。併發
Channel
Channel(通道)表示到實體,如硬件設備、文件、網絡套接字或能夠執行一個或多個不一樣 I/O 操做(如讀取或寫入)的程序組件的開放的鏈接。Channel接口的經常使用實現類有FileChannel(對應文件IO)、DatagramChannel(對應UDP)、SocketChannel和ServerSocketChannel(對應TCP的客戶端和服務器端)。Channel和IO中的Stream(流)是差很少一個等級的。只不過Stream是單向的,譬如:InputStream, OutputStream.而Channel是雙向的,既能夠用來進行讀操做,又能夠用來進行寫操做。socket
Selector
Selector(選擇器)用於監聽多個通道的事件(好比:鏈接打開,數據到達)。所以,單個的線程能夠監聽多個數據通道。即用選擇器,藉助單一線程,就可對數量龐大的活動I/O通道實施監控和維護。ide
Java NIO的簡單實現函數
public class Demo1 { private static Integer port = 8080; // 通道管理器(Selector) private static Selector selector; private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 1000, TimeUnit.MILLISECONDS, new LinkedTransferQueue<>(), new ThreadPoolExecutor.AbortPolicy()); public static void main(String[] args) { try { // 建立通道ServerSocketChannel ServerSocketChannel open = ServerSocketChannel.open(); // 將通道設置爲非阻塞 open.configureBlocking(false); // 綁定到指定的端口上 open.bind(new InetSocketAddress(port)); // 通道管理器(Selector) selector = Selector.open(); /** * 將通道(Channel)註冊到通道管理器(Selector),併爲該通道註冊selectionKey.OP_ACCEPT事件 * 註冊該事件後,當事件到達的時候,selector.select()會返回, * 若是事件沒有到達selector.select()會一直阻塞。 */ open.register(selector, SelectionKey.OP_ACCEPT); // 循環處理 while (true) { /** * 當註冊事件到達時,方法返回,不然該方法會一直阻塞 * 該Selector的select()方法將會返回大於0的整數,該整數值就表示該Selector上有多少個Channel具備可用的IO操做 */ int select = selector.select(); System.out.println("當前有 " + select + " 個channel能夠操做"); // 一個SelectionKey對應一個就緒的通道 Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { // 獲取事件 SelectionKey key = iterator.next(); // 移除事件,避免重複處理 iterator.remove(); // 客戶端請求鏈接事件,接受客戶端鏈接就緒 if (key.isAcceptable()) { accept(key); } else if (key.isReadable()) { // 監聽到讀事件,對讀事件進行處理 threadPoolExecutor.submit(new NioServerHandler(key)); } } } } catch (IOException e) { e.printStackTrace(); } } /** * 處理客戶端鏈接成功事件 * * @param key */ public static void accept(SelectionKey key) { try { // 獲取客戶端鏈接通道 ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel sc = ssc.accept(); sc.configureBlocking(false); // 給通道設置讀事件,客戶端監聽到讀事件後,進行讀取操做 sc.register(selector, SelectionKey.OP_READ); System.out.println("accept a client : " + sc.socket().getInetAddress().getHostName()); } catch (IOException e) { e.printStackTrace(); } } /** * 監聽到讀事件,讀取客戶端發送過來的消息 */ public static class NioServerHandler implements Runnable { private SelectionKey selectionKey; public NioServerHandler(SelectionKey selectionKey) { this.selectionKey = selectionKey; } @Override public void run() { try { if (selectionKey.isReadable()) { SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); // 從通道讀取數據到緩衝區 ByteBuffer buffer = ByteBuffer.allocate(1024); // 輸出客戶端發送過來的消息 socketChannel.read(buffer); buffer.flip(); System.out.println("收到客戶端" + socketChannel.socket().getInetAddress().getHostName() + "的數據:" + new String(buffer.array())); //將數據添加到key中 ByteBuffer outBuffer = ByteBuffer.wrap(buffer.array()); // 將消息回送給客戶端 socketChannel.write(outBuffer); selectionKey.cancel(); } } catch (IOException e) { e.printStackTrace(); } } } }