Java的IO、NIO、AIO瞭解

java提供了IO、NIO、AIO包,用於處理輸入輸出。

BIO:Block IO 同步阻塞式 IO,就是我們平常使用的傳統 IO,它的特點是模式簡單使用方便,併發處理能力低。
NIO:Non IO 同步非阻塞 IO,是傳統 IO 的升級,客戶端和服務器端通過 Channel(通道)通訊,實現了多路複用。
AIO:Asynchronous IO 是 NIO 的升級,也叫 NIO2,實現了異步非堵塞 IO ,異步 IO 的操作基於事件和回調機制
 

IO

IO就是我們常用的 java.io包,它基於流模型,提供了File 抽象、輸入輸出流等。因爲是同步阻塞的,也有人稱爲BIO。好處就是可靠,返回後可以立刻知曉結果。
按數據分:字符流(InputStream、OutputStream)、字節流。
按操作分:輸入流、輸出流。

注:IO流的緩衝區是用來提高效率的。

 

NIO

NIO的三個主要組成部分:Channel(通道)、Buffer(緩衝區)、Selector(選擇器)。它們關係可以簡單下圖說明:

 

Channel

可以通過它讀取和寫入數據,類似IO中的流,不過它可以用於讀、寫或者同時用於讀寫。一個Channel綁定一個Buffer。
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel

Buffer

NIO有如下 buffer:
ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer
這些Buffer類型代表了不同的數據類型。換句話說,就是可以通過char,short,int,long,float 或 double類型來操作緩衝區中的字節。

Selector

Selector 對象上可以註冊很多個Channel,監聽各個Channel上發生的事件,當事件發生時決定Channel讀寫。這樣使用Selector就可以做到用一個線程來處理所有的channels,畢竟線程之間的切換的代價很高。

創建selector

Selector selector = Selector.open();

註冊channel的事件

channel.configureBlocking(false);// 注意這裏channel一定要設置成異步模式,不然報錯
SelectionKey key = channel.register(selector,SelectionKey.OP_READ);

對應事件說明:

事件

功能

對應判斷方法

SelectionKey.OP_ACCEP

服務器監聽到了客戶連接,服務器可以接收這個連接

selectionKey.isAcceptable();

SelectionKey.OP_CONNECT

連接就緒事件,表示客戶端與服務器的連接已經建立成功

selectionKey.isConnectable();

SelectionKey.OP_READ

讀就緒事件,表示通道中已經有了可讀的數據,可以執行讀操作

selectionKey.isReadable();

SelectionKey.OP_WRITE

寫就緒事件,表示已經可以向通道寫數據

selectionKey.isWritable();

如果要處理比較大的數據,從性能考慮建議用MappedByteBuffer。

看下示例:

public class MyMain {

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

        ByteBuffer sendbuffer = ByteBuffer.allocate(2048);
        ByteBuffer receivebuffer = ByteBuffer.allocate(2048);

        // 1 創建一個 Selector用於註冊。當事件發生時,seletor 告訴你發生了什麼
        Selector selector = Selector.open();

        ServerSocketChannel socketChannel = ServerSocketChannel.open();
        socketChannel.configureBlocking(false);//  注意:一定要設置爲 非阻塞的

        ServerSocket socket = socketChannel.socket();
        socket.bind(new InetSocketAddress("127.0.0.1", 2181));// 綁定到給定的端口

        // 註冊 accept 事件。值得一說的是因爲Channel一定要是異步的,因此這裏不能用 FileChannel
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 這個方法會阻塞,直到至少有一個已註冊的事件發生
            selector.select();
            // 返回此選擇器的已選擇鍵集。
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey selectionKey = iterator.next();
                iterator.remove();
                handleKey(selector, selectionKey, sendbuffer, receivebuffer);
            }
        }
    }

    // 處理請求
    private static void handleKey(Selector selector, SelectionKey selectionKey, ByteBuffer sendBuffer, ByteBuffer receivebuffer) throws IOException {
        // 接受請求
        SocketChannel client = null;
        int count = 0;
        // 測試此鍵的通道是否已準備好接受新的套接字連接。
        if (selectionKey.isAcceptable()) {
            System.out.println("通道連接已可用");
            // 返回爲之創建此鍵的通道。
            ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();
            // 接受到此通道套接字的連接。
            // 此方法返回的套接字通道(如果有)將處於阻塞模式。
            client = server.accept();
            // 配置爲非阻塞
            client.configureBlocking(false);
            // 註冊到selector,等待連接
            client.register(selector, SelectionKey.OP_READ);
        } else if (selectionKey.isReadable()) {
            // 返回爲之創建此鍵的通道。
            client = (SocketChannel) selectionKey.channel();
            //將緩衝區清空以備下次讀取
            receivebuffer.clear();
            //讀取服務器發送來的數據到緩衝區中
            count = client.read(receivebuffer);
            if (count > 0) {
                System.out.println("從客戶端獲取的數據:" + new String(receivebuffer.array(), 0, count));
                client.register(selector, SelectionKey.OP_WRITE);
            }
        } else if (selectionKey.isWritable()) {
            System.out.println("可向客戶端寫數據");
            //將緩衝區清空以備下次寫入
            sendBuffer.clear();
            // 返回爲之創建此鍵的通道。
            client = (SocketChannel) selectionKey.channel();
            sendBuffer.put("Hellow world".getBytes());
            sendBuffer.flip();
            //輸出到通道
            client.write(sendBuffer);
            System.out.println("服務器端向客戶端發送數據");
            client.register(selector, SelectionKey.OP_READ);
        }
    }
}

啓動後,輸入 http://localhost:2181/ 後,就會在控制檯發現有請求和可讀時間發現

AIO

即java NIO2。NIO是同步非阻塞的,提升了性能。但還是異步的。NIO2是異步的,因此也稱即AIO,即Asynchronous IO。異步 IO 操作是基於事件和回調機制的。

看下例子:

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章