<input id="0qass"><u id="0qass"></u></input>
  • <input id="0qass"><u id="0qass"></u></input>
  • <menu id="0qass"><u id="0qass"></u></menu>

    NIO介紹

    I/O 模型

    I/O 模型基本說明

    • I/O 模型簡單的理解:就是用什么樣的通道進行數據的發送和接收,很大程度上決定了程序通信的性能
    • Java 共支持 3 種網絡編程模型 I/O 模式:BIO、NIO、AIO:

    Java BIO

    同步并阻塞(傳統阻塞型),服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器端就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的開銷

    在這里插入圖片描述

    Java NIO

    同步非阻塞,服務器實現模式為一個線程處理多個請求(連接),即客戶端發送的連接請求都會注冊到多路復用器上,多路復用器輪詢到連接有 I/O 請求就進行處理
    在這里插入圖片描述

    Java AIO

    異步非阻塞,AIO 引入同步通道的概念,采用了 Proator 模式,簡化了程序編寫,有效的請求才啟動線程,它的特點是先由操作系統完成后才通知服務器端程序啟動線程去處理,一般適用于連接數較多且連接時間較長的應用

    BIO、NIO、AIO 使用場景分析

    • BIO 方式適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發局限于應用中,JDK1.4 以前的唯一選擇,但程序簡單易理解
    • NIO 方式適用于連接數目多且連接比較短(輕操作)的架構,比如聊天服務器,彈幕系統,服務器間通訊等。編程比較復雜,JDK 1.4 開始支持
    • AIO 方式適用于連接數目多且連接比較長(重操作)的架構,比如相冊服務器,充分調用 OS 參與并發操作,編程比較復雜,JDK 7 開始支持

    Java BIO 基本介紹

    • Java BIO 就是傳統的 Java IO 編程,其相關的類和接口在 java.io
    • BIO(blocking I/O):同步阻塞,服務器實現模式為一個連接一個線程,即客戶端有連接請求時服務器就需要啟動一個線程進行處理,如果這個連接不做任何事情會造成不必要的線程開銷,可以通過線程池機制改善
    • BIO 方式適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,并發局限于應用中,JDK 1.4 以前的唯一選擇,程序簡單易理解

    簡單的 Java BIO 實例

    package nettydemo;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @author Woo_home
     * @create by 2020/3/31  0:16
     */
    public class BIOServer {
        public static void main(String[] args) throws IOException {
            // 線程池機制
    
            // 思路
            // 1、創建一個線程池
            // 2、如果有客戶端連接,就創建一個線程,與之通信(單獨寫一個方法)
    
            ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    
            // 創建 SocketServer
            ServerSocket serverSocket = new ServerSocket(6666);
    
            System.out.println("服務器啟動了");
    
            while (true) {
                // 監聽,等待客戶端連接
                final Socket socket = serverSocket.accept();
                System.out.println("連接到一個客戶端");
    
                // 就創建一個線程,與之通信(單獨寫一個方法)
                newCachedThreadPool.execute(new Runnable() {
                    @Override
                    public void run() { // 重寫 run 方法
                        // 可以和客戶端通訊
                        handler(socket);
                    }
                });
            }
        }
    
        // 編寫一個 Handler 方法,和客戶端通訊
        public static void handler(Socket socket) {
            try {
                System.out.println("線程信息 ID = " + Thread.currentThread().getId() + "名字 = " + Thread.currentThread().getName());
                byte[] bytes = new byte[1024];
                // 通過 Socket 獲取輸入流
                InputStream inputStream = socket.getInputStream();
    
                // 循環讀取客戶端發送的數據
                while (true) {
                    System.out.println("線程信息 ID = " + Thread.currentThread().getId() + "名字 = " + Thread.currentThread().getName());
                    int read = inputStream.read(bytes);
                    if (read != -1) {
                        System.out.println(new String(bytes,0,read)); // 輸出客戶端發送的數據
                    } else {
                        break;
                    }
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                System.out.println("關閉和 client 的連接");
                try {
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    運行該程序
    在這里插入圖片描述
    打開 cmd 輸入以下命令連接到我們編寫的 SocketServer,端口號是上面設置的端口號

    telnet 127.0.0.1 6666 
    

    在這里插入圖片描述
    SocketServer 端控制臺打印內容
    在這里插入圖片描述
    在 client 界面按住 CTRL + 中括號進行 client 端的消息發送,使用 send 命令
    在這里插入圖片描述
    回車
    在這里插入圖片描述然后 Server 端會顯示 Client 端發送過來的內容
    在這里插入圖片描述
    當啟動多個 cmd 時,Server 就會開啟多個線程,這里不再演示

    總結

    • 服務器啟動一個 ServerSocket
    • 客戶端啟動 Socket 對服務器進行通信,默認情況下服務器需要對每個客戶建立一個線程與之通訊
    • 客戶端發出請求后,先咨詢服務器是否有線程響應,如果沒有則會等待,或者被拒絕
    • 如果有響應,客戶端線程會等待請求結束后,再繼續執行

    Java NIO 基本介紹

    • Java NIO 全稱 Java non-blocking IO,是指 JDK 提供的新 API。從 JDK 1.4 開始,Java 提供了一系列改進的輸入 / 輸出的新特性,被統稱為 NIO(即 New IO),是 同步阻塞
    • NIO 相關類都被放在 java.nio 包及子包下,并且對原 java.io 包中的很多類進行改寫
    • NIO 有三大核心部分: Channel(通道)、Buffer(緩沖區)、Selector(選擇器)
    • NIO 是面向 緩沖區,或者面向塊 編程的,數據讀取到一個它稍后處理的緩沖區,需要時可在緩沖區中前后一道,這就增加了處理過程中的靈活性,使用它可以提供非阻塞式的高伸縮性網絡
    • Java NIO 的非阻塞模式,使一個線程從某通道發送請求或者讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取,而 不是保持線程阻塞,所以直至數據變得可以讀取之前,該線程可以繼續做其它的事情。非阻塞寫也是如此,一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情

    通俗理解:NIO 是可以做到用一個線程來處理多個操作的,假設有 10000 個請求過來,根據實際情況,可以分配 50 或者 100 個線程來處理,不像之前的阻塞 IO 那樣,非得分配 10000 個

    HTTP 2.0 使用了多路復用的技術,做到同一個連接并發處理多個請求,而且并發請求的數量比 HTTP 1.1 大了好幾個數量級

    代碼實現

    package nettydemo.nio;
    
    import java.nio.IntBuffer;
    
    /**
     * @author Woo_home
     * @create by 2020/3/31  23:43
     */
    public class BasicBuffer {
        public static void main(String[] args) {
    
            // 舉例說明 Buffer 的使用(簡單說明)
            // 創建一個 Buffer,大小為5,可以存放 5 個int
            IntBuffer intBuffer = IntBuffer.allocate(5);
    
            // 向 Buffer 中存放數據
            for (int i = 0; i < intBuffer.capacity(); i++) {
                intBuffer.put(i * 2);
            }
    
            // 如果從 Buffer 中讀取數據
            // 將 Buffer 轉換,讀寫切換
            intBuffer.flip();
    
    		// 讀取數據
            while (intBuffer.hasRemaining()) {
                System.out.println(intBuffer.get());
            }
        }
    }
    

    輸出:
    在這里插入圖片描述

    NIO 和 BIO 的比較

    • BIO 以 流的方式 處理數據,而 NIO 以 塊的方式 處理數據,塊 I/O 的效率比流 I/O 高很多
    • BIO 是阻塞的,NIO 則是非阻塞的
    • BIO 基于字節流和字符流進行操作的,而 NIO 基于 Channel(通道)和 Buffer(緩沖區)進行操作,數據總是從通道讀取到緩沖區中,或者從緩沖區寫入到通道中。Selector(選擇器)用于監聽多個通道的事件(比如:連接請求,數據到達等),因此使用單個線程就可以監聽多個客戶端連接
    ??2020 CSDN 皮膚主題: 技術黑板 設計師:CSDN官方博客 返回首頁
    多乐彩