Java NIO

基本概念

Java NIO(Native IO) 是JDK1.4 開始提供的新的API。為所有原始類型提供Buffer緩存,字符集編碼等解決方案。它通過一個本地的DirectBuffer來直接為APP分配內存,避免JVM參與其中。通過這樣的方式來提高效率

NIO模型

NIO是一個同步非阻塞的線程模型,同步是指線程不斷輪詢IO事件是否就緒,非阻塞是指線程在等待IO就緒之前,可以做其他的任務。同步核心為Selector,Selector 代替了線程對IO事件的輪詢,避免了阻塞同時減少了線程的消耗。非阻塞的核心是通道(Channel)和緩沖區(Buffer),當IO事件就緒時,就可以將數據寫進緩沖區,再由緩沖區交給線程。從而保證
IO的成功

NIO的三個核心組件

  • Channels : 通道是I/O傳輸過程中需要通過的入口,緩沖區是這些數據的傳輸的來源或者目的地
  1. 在NIO中如果想要將Data傳輸到目的地的Buffer中,則首先需要傳輸到目的地的Channel中。
  2. 然后目的地Buffer再從屬于自己的Channel中取出數據放置到Buffer中
  • Buffers:
  • 緩沖區,可以理解為DirectBuffer區域向線程輸出數據的緩沖地帶。
  • 當IO可用時而且數據到達時,可以預先寫入緩沖區。通過這樣的方式,線程就不需要特意的等待IO
  • Selectors: 顧名思義就是用來調度各個IO事件,Selector允許單線程處理多個Channel
  1. 首先向Selector注冊Channel
  2. 調用其select方法。這個方法會阻塞到某個注冊的通道有時間就緒(輪詢)
  3. 堅挺Selector感興趣的四個事件 OP_ACCEPT,OP_CONNECT,OP_READ,OP_WRITE

雖然Java NIO 中除此之外還有很多類和組件,但在我看來,Channel,Buffer 和 Selector 構成了核心的API。其它組件,如Pipe和FileLock,只不過是與三個核心組件共同使用的工具類。

基本使用

基于NIO的一個Server

Server 端


    public static void main(String[] args){
        try {
            //開啟一個Selector
            Selector selector = Selector.open();
            //Server端的Channel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            InetSocketAddress inetSocketAddress = new InetSocketAddress("localhost",8083);
            //監聽 localhost:8083 端口
            serverSocketChannel.bind(inetSocketAddress);
            //設置為非阻塞的模式
            serverSocketChannel.configureBlocking(false);
            //serverSocket 只能接收 新的連接,所以這個只會返回 {@link SelectionKey#OP_ACCEPT}.
            int ops = serverSocketChannel.validOps();
            //將Channel注冊到selector 上,返回的SelectionKey 標識著這個Channel的狀態,感興趣的Event,可用的event等
            SelectionKey selectionKey = serverSocketChannel.register(selector,ops);
            while (true){
                System.out.println("Waiting  for the Connections come");
                //將 準備進行IO操作的Channel集合返回。這是個阻塞的方法,當至少有一個Channel ready的時候才會返回!
                selector.select();
                //
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                selectionKeys.stream().forEach(key ->{
                    try {
                        //如果IO的Connector是可用的,那么
                        if(key.isAcceptable()){
                                //接收一個來自客戶端的請求,
                                SocketChannel socketClientChannel = serverSocketChannel.accept();
                                //因為這個已經設置了非阻塞式的,所以這里的連接可能會是null
                                if (socketClientChannel != null){
                                    socketClientChannel.configureBlocking(false);
                                    //將selector的OP_READ 注冊為clientSocket
                                    socketClientChannel.register(selector, SelectionKey.OP_READ);
                                }
                        }else if(key.isReadable()){
                            //因為我們前面將ClientChannel 注冊到OP_READ上面,所有這里的SelectorKey的channel一定是Client
                            SocketChannel crunchifyClient = (SocketChannel) key.channel();
                            if (crunchifyClient == null){
                                log("no connections valid sleep 100ms ");
                                Thread.sleep(100);
                            }
                            ByteBuffer crunchifyBuffer = ByteBuffer.allocate(1024);
                            //將Channel將Channel 中的數據寫入到Buffer中。。
                            crunchifyClient.read(crunchifyBuffer);
                            String result = new String(crunchifyBuffer.array()).trim();
                            log("Message received: " + result);
                            crunchifyClient.close();
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void log(String s) {
        System.out.println(s);
    }
}

Cilent 端

public class NIOClient implements Runnable {
    public NIOClient(String connectID) {
        this.connectID = connectID;
    }

    private String connectID;
    public static void main(String[] args) {
        for (int count = 0; count < 50;count++){
            new Thread(new NIOClient(""+count)).start();
        }

    }

    public static void log(String msg) {
        System.out.println(msg);
    }

    @Override
    public void run() {

        try {
            Selector selector = Selector.open();
            InetSocketAddress socketAddress = new InetSocketAddress("localhost", 8083);
            //
            //  Thread.sleep(1000); //每10ms發出一個
            SocketChannel socketChannel = SocketChannel.open(socketAddress);
            socketChannel.configureBlocking(false);
            socketChannel.register(selector, SelectionKey.OP_CONNECT);
            for (int index = 0; index < 100; index++) {
                byte[] bytes = new String("connectID" +connectID + "this is the  " + index + "data").getBytes();
                ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
                socketChannel.write(byteBuffer);
                byteBuffer.clear();
                Thread.sleep(2000);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Java NIO(New IO)是從Java 1.4版本開始引入的一個新的IO API,可以替代標準的Java I...
    JackChen1024閱讀 7,583評論 1 143
  • 這兩天了解了一下關于NIO方面的知識,網上關于這一塊的介紹只是介紹了一下基本用法,沒有系統的解釋NIO與阻塞、非阻...
    Ruheng閱讀 7,149評論 5 48
  • 簡介 Java NIO 是由 Java 1.4 引進的異步 IO.Java NIO 由以下幾個核心部分組成: Ch...
    永順閱讀 1,809評論 0 15
  • 概述 NIO主要有三大核心部分:Channel(通道),Buffer(緩沖區),Selector。 傳統IO基于...
    時之令閱讀 3,726評論 0 8
  • 前言: 之前的文章《Java文件IO常用歸納》主要寫了Java 標準IO要注意的細節和技巧,由于網上各種學習途徑,...
    androidjp閱讀 2,922評論 0 22