精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Netty學習前基本知識—BIO 、NIO 、AIO 總結

開發 前端
熟練掌握 BIO、NIO、AIO 的基本概念以及一些常見問題是你準備面試的過程中不可或缺的一部分,另外這些知識點也是你學習 Netty 的基礎。

 基本概念
IO模型就是說用什么樣的通道進行數據的發送和接收,Java 共支持3中網絡變成 IO 模式:BIO、NIO、AIO。Java 中的 BIO、NIO 和 AIO 理解為是 Java 語言對操作系統的各種 IO 模型的封裝。我們在使用這些 API 的時候,不需要關系操作系統層面的知識,也不需要根據不同操作系統編寫不同的代碼。

在講 BIO、NIO、AIO 之前先回顧幾個概念:同步與異步、阻塞與非阻塞、I/O模型。

同步與異步

  • 同步:同步就是發起一個調用后,被調用者未處理完請求之前,調用不返回。
  • 異步:異步就是發一個調用后,立刻得到被調用者的回應表示已接收到請求,但是被調用者并沒有返回結果,此時可以處理其他的請求,被調用者通常依靠事件、回調等機制來通知調用者其返回結果。

同步和異步的區別最大在于異步的話調用者不需要等待結果處理,被調用者會通過回調等機制來通知調用者返回結果。

阻塞和非阻塞

  • 阻塞:阻塞就是發起一個請求,調用者一直等待請求結果返回,也就是當前線程會被掛起,無法從事其他任務,只有當條件就緒才能繼續。
  • 非阻塞:非阻塞就是發起一個請求,調用者不用一直等著結果返回,可以先去干其他的事情。

同步異步與阻塞非阻塞(段子)
老張燒開水的故事(故事來源網絡)

老張愛喝茶,廢話不說,煮開水。

出場人物:老張,水壺兩把(普通水壺,簡稱水壺;會響的水壺,簡稱響水壺)。

1.老張把水壺放到火上,立等水開。(同步阻塞)
老張覺得自己有點傻

2.老張把水壺放到火上,去客廳看電視,時不時去廚房看看水開沒有。(同步非阻塞)
老張還是覺得自己有點傻,于是變高端了,買了把會響笛的那種水壺。水開之后,能大聲發出嘀~~的噪音。

3.老張把響水壺放到火上,立等水開。(異步阻塞)
老張覺得這樣傻等意義不大

4.老張把響水壺放到火上,去客廳看電視,水壺響之前不再去看它了,響了再去拿壺。(異步非阻塞)
老張覺得自己聰明了。

所謂同步異步,只是對于水壺而言

  • 普通水壺:同步;響水壺:異步。
  • 雖然都能干活,但響水壺可以在自己完工之后,提示老張水開了,這是普通水壺所不能及的。
  • 同步只能讓調用者去輪詢自己(情況2中),造成老張效率的低下。

所謂阻塞非阻塞,僅僅對于老張而言

  • 立等的老張:阻塞;看電視的老張:非阻塞。
  • 情況1 和 情況3 中老張就是阻塞的,媳婦喊他都不知道。雖然情況3中響水壺是異步的,可對于立等的老張沒有太大的意義。所以一般異步是配合非阻塞使用的,這樣才能發揮異步的效用。

常見的 I/O 模型對比
所有的系統 I/O 都分為兩個階段:等待就緒 和 操作。

舉例來說,讀函數,分為等待系統可讀和真正的讀;同理,寫函數分為等待網卡可以寫和真正的寫。

需要說明的是等待就緒的阻塞是不使用 CPU 的,是在“空等”;而真正的讀操作的阻塞是使用 CPU 的,真正在“干活”,而且這個過程非常快,屬于 memory copy,帶寬通常在 1GB/s 級別以上,可以理解為基本不耗時。

如下幾種常見 I/O 模型的對比:

以socket.read()為例子:

  • 傳統的BIO里面socket.read(),如果TCP RecvBuffer里沒有數據,函數會一直阻塞,直到收到數據,返回讀到的數據。
  • 對于NIO,如果TCP RecvBuffer有數據,就把數據從網卡讀到內存,并且返回給用戶;反之則直接返回0,永遠不會阻塞。
  • AIO(Async I/O)里面會更進一步:不但等待就緒是非阻塞的,就連數據從網卡到內存的過程也是異步的。

換句話說,BIO里用戶最關心“我要讀”,NIO里用戶最關心"我可以讀了",在AIO模型里用戶更需要關注的是“讀完了”。

NIO一個重要的特點是:socket主要的讀、寫、注冊和接收函數,在等待就緒階段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。

BIO(Blocking I/O)
同步阻塞 I/O 模式,數據的讀取寫入必須阻塞在一個線程內等待其完成(一個客戶端連接對于一個處理線程)。

傳統 BIO
BIO通信(一請求一應答)模型圖如下(圖源網絡):

采用 BIO 通信模型 的服務隊,通常由一個獨立的 Acceptor 線程負責監聽客戶端的連接。我們一般通過在 while(true) 循環中服務端會調用 accept() 方法等待客戶端連接的方式監聽請求,請求一旦接收到一個連接請求,就可以建立通信套接字在這個通信套接字上進行讀寫操作,此時不能再接收其他客戶端連接請求,只能等待當前連接的客戶端的操作執行完成,不過可以通過多線程來支持多個客戶端的連接,如上圖所示。

如果要讓 BIO 通信模型 能夠同時處理多個客戶端的請求,就必須使用多線程(要原因是 socket.accept()、 socket.read()、 socket.write() 涉及的三個主要函數都是同步阻塞的),當一個連接在處理 I/O 的時候,系統是阻塞的,如果是單線程的必然就掛死在哪里。開啟多線程,就可以讓CPU去處理更多的事情。也就是說它在接收到客戶端連接請求之后為每個客戶端創建一個新的線程進行鏈路處理,處理完成之后,通過輸出流返回給客戶端,線程銷毀。這就是典型的 一請求一應答通信模型。

其實這也是所有使用多線程的本質:

  • 利用多核
  • 當 I/O 阻塞系統,但 CPU 空閑的時候,可以利用多線程使用 CPU 資源。

我們可以設想以下如果連接不做任何的事情的話就會造成不必要的線程開銷,不過可以通過 線程池機制 改善,線程池還可以讓線程的創建和回收成本相對較低。例如使用FixedTreadPool 可以有效的控制來線程的最大數量,保證來系統有限的資源的控制,實現了N(客戶端請求數量):M(處理客戶端請求的線程數量)的偽異步I/O模型(N可以遠遠大于M)。

我們再設想以下當客戶端并發訪問量增加后這種模型會出什么問題? 隨著并發訪問量增加會導致線程數急劇膨脹可能會導致線程堆棧溢出、創建新線程失敗等問題,最終導致進程宕機或者僵死,不能對外提供服務。

在Java虛擬機中,線程是寶貴的資源,主要體現在:

1.線程的創建和銷毀成本很高,尤其在 Linux 操作系統中,線程本質上就是一個進程,創建和銷毀線程都是重量級的系統函數;
2.線程本身占用較大內存,像 Java 的線程棧,一般至少分配 512k~1M 的空間,如果系統中的線程數過千,恐怕整個 JVM 的內存都會被吃掉一半;
3.線程的切換成本也很高。操作系統發生線程切換的時候,需要保留線程的上下文,然后執行系統調用。如果線程數過高,可能執行線程切換的時間甚至會大于線程執行的時間,這時候帶來的表現往 往是系統load偏高,CPU sy 使用率特別高(超過20%以上),導致系統幾乎陷入不可用的狀態;
4.容易造成鋸齒狀的系統負載。因為系統的負載是用活動線程數和CPU核心數,一旦線程數量高但外部網絡環境不是很穩定,就很容易造成大量請求的結果同時返回,激活大量阻塞線程從而使系統負載壓力過大。 Linux系統中CPU中sy過高> sy的值表示是內核的消耗,如果出現sy的值過高,不要先去考慮是內核的問題,先查看是不是內存不足,是不是磁盤滿,是不是IO的問題,就是說先考慮自己進程的問題,比方是否IO引起的問題,是否網絡引起的問題的。排查系統IO或者網絡等是否已經到瓶頸了。
偽異步 I/O
為了解決同步阻塞I/O面臨的一個鏈路需要一個線程處理的問題,后來有人對它的線程模型進行了優化:后端通過一個線程池來處理多個客戶端的請求接入,形成客戶端個數M:線程池最大線程數N的比例關系,其中M可以遠遠大于N.通過線程池可以靈活地調配線程資源,設置線程的最大值,防止由于海量并發接入導致線程耗盡。

偽異步IO模型圖(圖源網絡)

采用線程池和任務隊列可以實現一種叫做偽異步的 I/O 通信框架,它的模型圖如上圖所示。當有新的客戶端接入時,將客戶端的 Socket 封裝成一個Task(該任務實現java.lang.Runnable接口)投遞到后端的線程池中進行處理,JDK 的線程池維護一個消息隊列和 N 個活躍線程,對消息隊列中的任務進行處理。由于線程池可以設置消息隊列的大小和最大線程數,因此,它的資源占用是可控的,無論多少個客戶端并發訪問,都不會導致資源的耗盡和宕機。

偽異步I/O通信框架采用了線程池實現,因此避免了為每個請求都創建一個獨立線程造成的線程資源耗盡問題。不過因為它的底層仍然是同步阻塞的BIO模型,因此無法從根本上解決問題。

缺點

  1. IO 代碼里 read 操作是阻塞操作,如果連接不做數據讀寫操作會導致線程阻塞,浪費資源;
  2. 如果線程很多,會導致服務器線程太多,壓力太大。

應用場景
BIO 方式適用于連接數目比較小且固定的架構,這種方式對服務器資源要求比較高,但程序簡單理解。

BIO 代碼示例
服務端

  1. package com.niuh.bio; 
  2. import java.io.IOException; 
  3. import java.net.ServerSocket;import java.net.Socket;public class SocketServer {    public static void main(String[] args) throws IOException {        ServerSocket serverSocket = new ServerSocket(9000); 
  4.         while (true) { 
  5.             System.out.println("等待連接。。"); 
  6.             //阻塞方法            final Socket socket = serverSocket.accept();            System.out.println("有客戶端連接了。。"); 
  7.             // 多線程處理            new Thread(new Runnable() {                @Override                public void run() {                    try {                        handler(socket);                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }).start();            // 單線程處理            //handler(socket);        }    }    private static void handler(Socket socket) throws IOException {        System.out.println("thread id = " + Thread.currentThread().getId()); 
  8.         byte[] bytes = new byte[1024]; 
  9.         System.out.println("準備read。。"); 
  10.         //接收客戶端的數據,阻塞方法,沒有數據可讀時就阻塞        int read = socket.getInputStream().read(bytes); 
  11.         System.out.println("read完畢。。"); 
  12.         if (read != -1) { 
  13.             System.out.println("接收到客戶端的數據:" + new String(bytes, 0, read)); 
  14.             System.out.println("thread id = " + Thread.currentThread().getId()); 
  15.         }        socket.getOutputStream().write("HelloClient".getBytes()); 
  16.         socket.getOutputStream().flush(); 
  17.     }} 

客戶端

  1. package com.niuh.bio; 
  2. import java.io.IOException; 
  3. import java.net.Socket; 
  4. public class SocketClient { 
  5.     public static void main(String[] args) throws IOException { 
  6.         Socket socket = new Socket("127.0.0.1", 9000); 
  7.         //向服務端發送數據 
  8.         socket.getOutputStream().write("HelloServer".getBytes()); 
  9.         socket.getOutputStream().flush(); 
  10.         System.out.println("向服務端發送數據結束"); 
  11.         byte[] bytes = new byte[1024]; 
  12.         //接收服務端回傳的數據 
  13.         socket.getInputStream().read(bytes); 
  14.         System.out.println("接收到服務端的數據:" + new String(bytes)); 
  15.         socket.close(); 
  16.     } 

NIO(Non Blocking IO)
同步非阻塞,服務器實現模式為一個線程可以處理多個請求(連接),客戶端發送的連接請求都會注冊到 多路復用器 selector 上,多路復用器輪詢到連接有 IO 請求就進行處理。

它支持面向緩沖的,基于通道的I/O操作方法。NIO提供了與傳統BIO模型中的 Socket 和 ServerSocket 相對應的 SocketChannel 和 ServerSocketChannel 兩種不同的套接字通道實現,兩種通道都支持阻塞和非阻塞兩種模式。

  • 阻塞模式使用就像傳統中的支持一樣,比較簡單,但是性能和可靠性都不好;
  • 非阻塞模式正好與之相反。

對于低負載、低并發的應用程序,可以使用同步阻塞I/O來提升開發速率和更好的維護性; 對于高負載、高并發的(網絡)應用,應使用 NIO 的非阻塞模式來開發。

NIO核心組件
NIO 有三大核心組件:

  • Channel(通道)
  • Buffer(緩沖區)
  • Selector(選擇器)

整個NIO體系包含的類遠遠不止這三個,只能說這三個是NIO體系的“核心API”。

  1. channel 類似于流,每個 channel 對應一個 buffer 緩沖區,buffer 底層就是個數組;
  2. channel 會注冊到 selector 上,由 selector 根據 channel 讀寫事件的發生將其交由某個空閑的線程處理;
  3. selector 可以對應一個或多個線程
  4. NIO 的 Buffer 和 channel 既可以讀也可以寫

NIO的特性
我們從一個問題來總結:NIO 與 IO 的區別?

如果是在面試中來回答這個問題,我覺得首先肯定要從 NIO 流是非阻塞 IO,而 IO 流是阻塞 IO說起。然后可以從 NIO 的3個核心組件/特性為 NIO 帶來的一些改進來分析。

IO流是阻塞的,NIO流不是阻塞的
Java NIO 使我們可以進行非阻塞 IO 操作。比如說,單線程中從通道讀取數據到 buffer,同時可以繼續做別的事情,當數據讀取到 buffer 中后,線程再繼續處理數據。寫數據也是一樣的。另外,非阻塞寫也是日常,一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。

Java IO 的各種流是阻塞的,這意味著,當一個線程調用 read() 或 write() 時,該線程被阻塞,直到有一些數據被讀取或數據完全寫入。該線程在此期間不能再干任何事情了。

IO 面向流(Stream oriented),NIO 面向緩沖區(Buffer oriented)
Buffer(緩沖區)

Buffer 是一個對象,它包含一些要寫入或者要讀出的數據。在 NIO 類庫中加入 Buffer對象,體現了新庫與原庫 I/O的一個重要區別:

  • 在面向流的 I/O 中,可以直接將數據寫入或者將數據直接讀到 Stream 對象中。雖然 Stream 中也有 Buffer 開通的擴展類,但只是流的包裝類,還從流讀到緩沖區。
  • NIO 是直接讀到 Buffer 中進行操作。在 NIO 庫中,所有的數據都是用緩沖區處理的。在讀取數據時,它是直接讀到緩沖區中的;在寫入數據時,寫入到緩存中。任何時候訪問 NIO 中的數據,都是通過緩沖區進行操作。

最常用的緩沖區是 ByteBuffer,ByteBuffer 提供流一組功能用于操作 byte 數組。除了 ByteBuffer 還有其他的一些緩沖區,事實上,每一種 Java 基本類型(除了 Boolean 類型)都對應有一種緩沖區。

NIO 通過 Channel(通道)進行讀寫
Channel(通道)

通道是雙向的,可讀也可以寫,而流的讀寫是單向的。無論讀寫,通道只能和 Buffer 交互。因為 Buffer,通道可以異步地讀寫。

NIO 有選擇器,而 IO 沒有
Selectors(選擇器)

選擇器用于使用單線程處理多個通道。因此,它需要較少的線程來處理這些通道。線程之間的切換對于操作系統來說是昂貴的。因此,為了提供系統效率選擇器是有用的。

NIO 讀數據和寫數據
通常來說 NIO 中的所有 IO 都是從 Channel(通道)開始的。

  • 從通道進行數據讀取:創建一個緩沖區,然后請求通道讀取數據;
  • 從通道進行數據寫入:創建一個緩沖區,填充數據,并要求通道寫入數據。

數據讀取和寫入操作如下:

應用場景
NIO 方式適用于連接數目多且連接比較短(輕操作)的架構,比如聊天服務器、彈幕系統、服務器間通訊、編程比較復雜。

NIO 代碼示例
服務端

  1. package com.niuh.nio; 
  2. import java.io.IOException; 
  3. import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.Iterator;public class NIOServer {    //public static ExecutorService pool = Executors.newFixedThreadPool(10); 
  4.     public static void main(String[] args) throws IOException {        // 創建一個在本地端口進行監聽的服務Socket通道.并設置為非阻塞方式        ServerSocketChannel ssc = ServerSocketChannel.open(); 
  5.         //必須配置為非阻塞才能往selector上注冊,否則會報錯,selector模式本身就是非阻塞模式        ssc.configureBlocking(false); 
  6.         ssc.socket().bind(new InetSocketAddress(9000)); 
  7.         // 創建一個選擇器selector        Selector selector = Selector.open(); 
  8.         // 把ServerSocketChannel注冊到selector上,并且selector對客戶端accept連接操作感興趣        ssc.register(selector, SelectionKey.OP_ACCEPT);        while (true) { 
  9.             System.out.println("等待事件發生。。"); 
  10.             // 輪詢監聽channel里的keyselect是阻塞的,accept()也是阻塞的 
  11.             int select = selector.select(); 
  12.             System.out.println("有事件發生了。。"); 
  13.             // 有客戶端請求,被輪詢監聽到            Iterator<SelectionKey> it = selector.selectedKeys().iterator();            while (it.hasNext()) { 
  14.                 SelectionKey key = it.next(); 
  15.                 //刪除本次已處理的key,防止下次select重復處理 
  16.                 it.remove(); 
  17.                 handle(key);            }        }    }    private static void handle(SelectionKey key) throws IOException {        if (key.isAcceptable()) { 
  18.             System.out.println("有客戶端連接事件發生了。。"); 
  19.             ServerSocketChannel ssc = (ServerSocketChannel) key.channel();            //NIO非阻塞體現:此處accept方法是阻塞的,但是這里因為是發生了連接事件,所以這個方法會馬上執行完,不會阻塞            //處理完連接請求不會繼續等待客戶端的數據發送            SocketChannel sc = ssc.accept();            sc.configureBlocking(false); 
  20.             //通過Selector監聽Channel時對讀事件感興趣            sc.register(key.selector(), SelectionKey.OP_READ);        } else if (key.isReadable()) { 
  21.             System.out.println("有客戶端數據可讀事件發生了。。"); 
  22.             SocketChannel sc = (SocketChannel) key.channel();            ByteBuffer buffer = ByteBuffer.allocate(1024); 
  23.             //NIO非阻塞體現:首先read方法不會阻塞,其次這種事件響應模型,當調用到read方法時肯定是發生了客戶端發送數據的事件 
  24.             int len = sc.read(buffer); 
  25.             if (len != -1) { 
  26.                 System.out.println("讀取到客戶端發送的數據:" + new String(buffer.array(), 0, len)); 
  27.             }            ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes()); 
  28.             sc.write(bufferToWrite); 
  29.             key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);        } else if (key.isWritable()) { 
  30.             SocketChannel sc = (SocketChannel) key.channel();            System.out.println("write事件"); 
  31.             // NIO事件觸發是水平觸發            // 使用Java的NIO編程的時候,在沒有數據可以往外寫的時候要取消寫事件,            // 在有數據往外寫的時候再注冊寫事件            key.interestOps(SelectionKey.OP_READ);            //sc.close(); 
  32.         }    }} 

NIO服務端程序詳細分析:

  • 創建一個 ServerSocketChannel 和 Selector ,并將 ServerSocketChannel 注冊到 Selector 上;
  • Selector 通過 select() 方法監聽 channel 事件,當客戶端連接時,selector 監聽到連接事件,獲取到 ServerSocketChannel 注冊時綁定的 selectionKey;
  • selectionKey 通過 channel() 方法可以獲取綁定的 ServerSocketChannel;
  • ServerSocketChannel 通過 accept() 方法得到 SocketChannel;
  • 將 SocketChannel 注冊到 Selector 上,關心 read 事件;
  • 注冊后返回一個 SelectionKey,會和該 SocketChannel 關聯;
  • Selector 繼續通過 select() 方法監聽事件,當客戶端發送數據給服務端,Selector 監聽到 read 事件,獲取到 SocketChannel 注冊時綁定的 SelectionKey;
  • SelectionKey 通過 channel() 方法可以獲取綁定的 socketChannel;
  • 將 socketChannel 里的數據讀取出來;
  • 用 socketChannel 將服務端數據寫回客戶端。

客戶端

  1. package com.niuh.nio; 
  2. import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Iterator;public class NioClient { 
  3.     //通道管理器 
  4.     private Selector selector; 
  5.     /**     * 啟動客戶端測試     *     * @throws IOException     */ 
  6.     public static void main(String[] args) throws IOException { 
  7.         NioClient client = new NioClient(); 
  8.         client.initClient("127.0.0.1", 9000); 
  9.         client.connect(); 
  10.     } 
  11.     /** 
  12.      * 獲得一個Socket通道,并對該通道做一些初始化的工作     *     * @param ip   連接的服務器的ip     * @param port 連接的服務器的端口號     * @throws IOException     */ 
  13.     public void initClient(String ip, int port) throws IOException { 
  14.         // 獲得一個Socket通道 
  15.         SocketChannel channel = SocketChannel.open(); 
  16.         // 設置通道為非阻塞 
  17.         channel.configureBlocking(false); 
  18.         // 獲得一個通道管理器 
  19.         this.selector = Selector.open(); 
  20.         // 客戶端連接服務器,其實方法執行并沒有實現連接,需要在listen()方法中調 
  21.         //用channel.finishConnect() 才能完成連接 
  22.         channel.connect(new InetSocketAddress(ip, port)); 
  23.         //將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_CONNECT事件。 
  24.         channel.register(selector, SelectionKey.OP_CONNECT); 
  25.     } 
  26.     /** 
  27.      * 采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理     *     * @throws IOException     */ 
  28.     public void connect() throws IOException { 
  29.         // 輪詢訪問selector 
  30.         while (true) { 
  31.             selector.select(); 
  32.             // 獲得selector中選中的項的迭代器 
  33.             Iterator<SelectionKey> it = this.selector.selectedKeys().iterator(); 
  34.             while (it.hasNext()) { 
  35.                 SelectionKey key = (SelectionKey) it.next(); 
  36.                 // 刪除已選的key,以防重復處理 
  37.                 it.remove(); 
  38.                 // 連接事件發生 
  39.                 if (key.isConnectable()) { 
  40.                     SocketChannel channel = (SocketChannel) key.channel(); 
  41.                     // 如果正在連接,則完成連接 
  42.                     if (channel.isConnectionPending()) { 
  43.                         channel.finishConnect(); 
  44.                     } 
  45.                     // 設置成非阻塞 
  46.                     channel.configureBlocking(false); 
  47.                     //在這里可以給服務端發送信息哦 
  48.                     ByteBuffer buffer = ByteBuffer.wrap("HelloServer".getBytes()); 
  49.                     channel.write(buffer); 
  50.                     //在和服務端連接成功之后,為了可以接收到服務端的信息,需要給通道設置讀的權限。 
  51.                     channel.register(this.selector, SelectionKey.OP_READ);                                            // 獲得了可讀的事件 
  52.                 } else if (key.isReadable()) { 
  53.                     read(key); 
  54.                 } 
  55.             } 
  56.         } 
  57.     } 
  58.     /** 
  59.      * 處理讀取服務端發來的信息 的事件     *     * @param key     * @throws IOException     */ 
  60.     public void read(SelectionKey key) throws IOException { 
  61.         //和服務端的read方法一樣 
  62.         // 服務器可讀取消息:得到事件發生的Socket通道 
  63.         SocketChannel channel = (SocketChannel) key.channel(); 
  64.         // 創建讀取的緩沖區 
  65.         ByteBuffer buffer = ByteBuffer.allocate(1024); 
  66.         int len = channel.read(buffer); 
  67.         if (len != -1) { 
  68.             System.out.println("客戶端收到信息:" + new String(buffer.array(), 0, len)); 
  69.         } 
  70.     } 

總結
NIO 模型的 selector 就像一個大總管,負責監聽各種 I/O 事件,然后轉交給后端線程去處理。

NIO 相對于 BIO 非阻塞的體現就在:BIO 的后端線程需要阻塞等待客戶端寫數據(比如 read 方法),如果客戶端不寫數據線程就要阻塞。

NIO 把等到客戶端操作的時候交給了大總管 selector ,selector 負責輪詢所有已注冊的客戶端,發現有事件發生了才轉交給后端線程處理,后端線程不需要做任何阻塞等待,直接處理客戶端事件的數據即可,處理完馬上結束,或返回線程池供其他客戶端事件繼續使用。還有就是 channel 的讀寫是非阻塞的。

Redis 就是典型的 NIO 線程模型,selector 收集所有的事件并且轉給后端線程,線程連續執行所有事件命令并將結果寫回客戶端。

AIO(Asynchronous I/O)
異步非阻塞, 由操作系統完成后回調通知服務端程序啟動線程去處理, 一般適用于連接數較多且連接時間較長的應用。

AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改進版 NIO 2,它是異步非阻塞的IO模型。異步 IO 是基于事件和回調機制實現的,也就是應用操作之后會直接返回,不會堵塞在那里,當后臺處理完成,操作系統會通知相應的線程進行后續的操作。

AIO 是異步IO的縮寫,雖然 NIO 在網絡操作中,提供了非阻塞的方法,但是 NIO 的 IO 行為還是同步的。對于 NIO 來說,我們的業務線程是在 IO 操作準備好時,得到通知,接著就由這個線程自行進行 IO 操作,IO操作本身是同步的。(除了 AIO 其他的 IO 類型都是同步的)

應用場景
AIO 方式適用于連接數目多且連接比較長(重操作)的架構。

AIO 代碼示例
服務端

  1. package com.niuh.aio; 
  2. import java.io.IOException; 
  3. import java.net.InetSocketAddress; 
  4. import java.nio.ByteBuffer; 
  5. import java.nio.channels.AsynchronousServerSocketChannel; 
  6. import java.nio.channels.AsynchronousSocketChannel; 
  7. import java.nio.channels.CompletionHandler; 
  8. public class AIOServer { 
  9.     public static void main(String[] args) throws Exception { 
  10.         final AsynchronousServerSocketChannel serverChannel = 
  11.                 AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(9000)); 
  12.         serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() { 
  13.             @Override 
  14.             public void completed(final AsynchronousSocketChannel socketChannel, Object attachment) { 
  15.                 try { 
  16.                     // 再此接收客戶端連接,如果不寫這行代碼后面的客戶端連接連不上服務端 
  17.                     serverChannel.accept(attachment, this); 
  18.                     System.out.println(socketChannel.getRemoteAddress()); 
  19.                     ByteBuffer buffer = ByteBuffer.allocate(1024); 
  20.                     socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() { 
  21.                         @Override 
  22.                         public void completed(Integer result, ByteBuffer buffer) { 
  23.                             buffer.flip(); 
  24.                             System.out.println(new String(buffer.array(), 0, result)); 
  25.                             socketChannel.write(ByteBuffer.wrap("HelloClient".getBytes())); 
  26.                         } 
  27.                         @Override 
  28.                         public void failed(Throwable exc, ByteBuffer buffer) { 
  29.                             exc.printStackTrace(); 
  30.                         } 
  31.                     }); 
  32.                 } catch (IOException e) { 
  33.                     e.printStackTrace(); 
  34.                 } 
  35.             } 
  36.             @Override 
  37.             public void failed(Throwable exc, Object attachment) { 
  38.                 exc.printStackTrace(); 
  39.             } 
  40.         }); 
  41.         Thread.sleep(Integer.MAX_VALUE); 
  42.     } 

客戶端

  1. package com.niuh.aio; 
  2. import java.net.InetSocketAddress; 
  3. import java.nio.ByteBuffer; 
  4. import java.nio.channels.AsynchronousSocketChannel; 
  5. public class AIOClient { 
  6.     public static void main(String... args) throws Exception { 
  7.         AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open();        socketChannel.connect(new InetSocketAddress("127.0.0.1", 9000)).get(); 
  8.         socketChannel.write(ByteBuffer.wrap("HelloServer".getBytes())); 
  9.         ByteBuffer buffer = ByteBuffer.allocate(512); 
  10.         Integer len = socketChannel.read(buffer).get();        if (len != -1) { 
  11.             System.out.println("客戶端收到信息:" + new String(buffer.array(), 0, len)); 
  12.         }    }} 

BIO、NIO、AIO對比

 

 

 

 

 

責任編輯:姜華 來源: 今日頭條
相關推薦

2022-04-16 16:52:24

Netty網絡服務器客戶端程序

2020-04-16 15:20:43

PHP前端BIO

2019-10-18 08:22:43

BIONIOAIO

2020-10-14 08:50:38

搞懂 Netty 線程

2023-07-11 08:40:02

IO模型后臺

2011-11-23 10:35:53

2010-07-28 15:52:00

Flex

2021-08-12 18:48:31

響應式編程Bio

2021-06-11 17:26:06

代碼Java網絡編程

2023-06-26 07:39:10

2017-04-12 11:47:32

2009-06-26 14:42:08

2017-10-20 22:40:54

電線電纜導體

2021-12-27 10:20:46

JavaNetty網絡

2009-06-11 14:40:32

Java list

2010-05-06 17:41:19

Unix命令

2012-06-05 00:41:07

JavaJava內存

2017-02-20 23:05:14

JavaScript

2009-12-22 14:43:38

Linux操作系統

2010-06-13 13:49:14

點贊
收藏

51CTO技術棧公眾號

日韩成人av网站| 久久精品国产96久久久香蕉| 国产v片免费观看| 欧美男男激情freegay| 日韩精品久久久久久| 最近日韩中文字幕中文| 日韩高清一二三区| 久久91导航| 亚洲精品成人精品456| 精品一区日韩成人| 亚洲天堂999| 精品91在线| 亚洲性视频网址| 中文字幕一区二区三区人妻在线视频| 伊人久久综合一区二区| 中文字幕一区在线| 免费观看成人高| 精品人妻一区二区三区换脸明星| 免费日韩av片| 久久久久久九九九| 成人午夜免费影院| 婷婷五月色综合香五月| 51精品国自产在线| 欧美少妇性生活视频| 毛片在线网址| 亚洲视频一区二区在线观看| 欧美精品一区在线发布| 性一交一乱一乱一视频| 美女视频网站黄色亚洲| 91po在线观看91精品国产性色| 少妇视频一区二区| 国产一区二区欧美| 亚洲精品国产欧美| 日本黄色大片在线观看| 亚洲男女网站| 欧美在线小视频| 国产免费黄视频| 国产黄色大片在线观看| 一区二区三区四区不卡在线| 亚洲精品一区二区三区蜜桃久| 人妻丰满熟妇av无码区hd| 国产在线精品一区二区夜色 | 在线免费看黄视频| 国产精品任我爽爆在线播放| 一本一本大道香蕉久在线精品 | 欧美极品aaaaabbbbb| 激情综合网站| 亚洲美女视频网| 黄色性生活一级片| 麻豆一区二区| 日韩国产激情在线| 538国产视频| 精品国产18久久久久久洗澡| 欧美一卡二卡| 国产女主播视频一区二区| 鲁丝一区鲁丝二区鲁丝三区| 无码精品一区二区三区在线| 99免费精品在线| 精品免费国产| 日韩精品系列| 久久看人人爽人人| 日本精品一区二区三区视频| 国产在线资源| 国产精品免费网站在线观看| 亚洲国产精品www| 青青影院在线观看| 亚洲乱码国产乱码精品精可以看| 中文字幕乱码免费| 手机在线免费看av| 亚洲不卡av一区二区三区| 日本人体一区二区| 欧美极度另类| 亚洲少妇中出一区| 免费看欧美一级片| sm捆绑调教国产免费网站在线观看| 亚洲成人一区二区| 黄色动漫在线免费看| 国产亚洲一区二区手机在线观看 | 国产亚洲欧美激情| 在线不卡日本| 欧美大胆的人体xxxx| 亚洲深深色噜噜狠狠爱网站| 成人av影院在线| 91精品国自产在线观看 | 亚洲福利视频久久| 蜜桃无码一区二区三区| 久久免费大视频| 欧美日韩高清区| 精品国产xxx| 国内精品免费在线观看| 国产一区二区久久久| av天在线观看| 亚洲一区成人在线| 欧美三级午夜理伦三级| 视频欧美精品| 日韩不卡在线观看| 蜜桃av.com| 一本色道久久| 91在线精品视频| 麻豆国产在线播放| 亚洲综合男人的天堂| 北条麻妃在线观看| 视频一区国产| 亚洲香蕉成视频在线观看| 久久国产精品波多野结衣| 天堂午夜影视日韩欧美一区二区| 91精品久久久久| 亚洲欧美日本在线观看| 亚洲女人的天堂| 久久久久免费精品| 动漫视频在线一区| 久久精品国产v日韩v亚洲| 天天做天天爱夜夜爽| 欧美a级在线观看| 成人午夜激情影院| 在线成人av电影| 不卡一二三区| 亚洲精品一区二区三区香蕉| 色撸撸在线视频| 久久不射2019中文字幕| 2014国产精品| 日本暖暖在线视频| 黑人巨大精品欧美一区免费视频 | 国产亚洲综合av| 成人午夜精品久久久久久久蜜臀| 亚洲精品tv| 国产亚洲一区精品| 久草视频一区二区| 99国产精品久久| 妞干网在线观看视频| 狂野欧美xxxx韩国少妇| 一区二区亚洲精品国产| 九九热精品视频在线| 成人福利在线看| 永久免费看av| 性欧美video另类hd尤物| 亚洲视频第一页| 国产熟妇一区二区三区四区| 成人av资源网站| 久久99久久99精品| 一区二区三区四区高清视频| 久久亚洲一区二区三区四区五区高 | av在线中文| 欧美午夜激情在线| 国产精品无码永久免费不卡| 在线欧美亚洲| 国产精品一区二区av| 欧美v亚洲v| 精品国产一区二区精华| 久久久久久久久99| 国产69精品久久久久777| 国产freexxxx性播放麻豆| 在线观看视频一区二区三区| 欧美另类极品videosbest最新版本| 国产精品久久影视| 尤物视频一区二区| 精品久久久久一区二区| 在线不卡亚洲| 极品校花啪啪激情久久| 在线观看特色大片免费视频| 亚洲欧洲成视频免费观看| 国产情侣小视频| 国产精品你懂的在线| 男女视频在线观看网站| 欧美成人69av| 精品久久精品久久| 日韩精品影院| 色老头一区二区三区在线观看| 一级全黄少妇性色生活片| 亚洲欧美色综合| www.17c.com喷水少妇| 一区二区三区四区五区精品视频| 欧美日韩免费高清| 亚洲高清影院| 久久免费视频在线| 久色视频在线| 91精品国产免费| 国产精品成人网站| 国产午夜精品久久久久久免费视 | 久久99热精品这里久久精品| 天天干天天摸天天操| 在线视频一区二区三| 777777国产7777777| 黄色免费在线网站| 成人18精品视频| 日韩免费毛片视频| 一区二区三区午夜视频| 精品久久久久久乱码天堂| 国产精品久久久久77777丨| 欧美精品一区在线播放| 免费在线国产| 日韩欧美电影在线| 无码人妻精品一区二区蜜桃色欲| ...av二区三区久久精品| 亚洲 欧美 日韩在线| 免费高清不卡av| 成年女人18级毛片毛片免费 | 亚洲成人中文字幕| 日韩国产亚洲欧美| 亚洲一区在线看| 呻吟揉丰满对白91乃国产区| 成人久久视频在线观看| 久久婷五月综合| 国产免费成人| 国产a级片免费看| 国产精品密蕾丝视频下载| 2019中文在线观看| 污视频在线免费观看网站| 亚洲毛片在线免费观看| 亚洲av综合色区无码一二三区| 一本大道综合伊人精品热热 | 伊人在线视频| 日韩精品在线视频观看| 精品美女www爽爽爽视频| 久久精品99国产国产精| xxx欧美精品| 深夜福利视频在线免费观看| 欧美一级免费大片| 亚洲午夜精品久久久| 欧美性xxxx| 日本三级视频在线| 亚洲老司机在线| 很污很黄的网站| 中文字幕乱码亚洲精品一区| 成人免费av片| 99re热这里只有精品视频| 日本黄色www| 韩国精品久久久| 在线观看国产中文字幕| 久久久久久网| 日本精品免费在线观看| 国精品一区二区三区| 天堂av免费看| 婷婷久久国产对白刺激五月99| 日韩欧美精品久久| 沈樵精品国产成av片| 精品一区在线播放| 加勒比色老久久爱综合网| 粉嫩av一区二区三区免费观看| **日韩最新| 91亚洲精品在线| 精品国产乱码久久久久久樱花| 成人xvideos免费视频| 欧美黄色成人| 91久久嫩草影院一区二区| 亚洲精品伊人| 亚洲综合社区网| 在这里有精品| 国产女主播一区二区| 超碰精品在线| 国内精品久久国产| 日韩深夜影院| 欧美另类视频在线| 国产探花一区| 亚洲无玛一区| 综合视频在线| 精品久久久久久无码中文野结衣| 很黄很黄激情成人| 久久精品国产sm调教网站演员| 亚洲美女色禁图| 欧美 激情 在线| 日本午夜精品视频在线观看| 成年网站在线播放| 激情综合网激情| 无码人妻久久一区二区三区蜜桃| 成人的网站免费观看| 草草影院第一页| 国产精品伦理在线| 精品国产乱码久久久久久鸭王1 | 亚洲国产精品久久久久秋霞蜜臀| 亚洲av永久纯肉无码精品动漫| 亚洲护士老师的毛茸茸最新章节| 偷拍精品一区二区三区| 日韩精品中文字幕在线| 成人在线观看黄色| 欧美xxxx14xxxxx性爽| 僵尸再翻生在线观看免费国语| 日本高清视频一区| 国产精品美女久久久久人| 国产精品乱码| 欧美三级三级| 日韩精品一区二区三区四| 国产日韩一区| 亚洲免费黄色网| 成人av网站在线| 999久久久国产| 五月婷婷综合网| 在线观看亚洲国产| 精品福利二区三区| 超碰免费97在线观看| 九九久久精品一区| 亚洲伦乱视频| 操人视频欧美| 日韩中文欧美| 国产极品尤物在线| 精品一区二区国语对白| 成人无码www在线看免费| 国产精品国产三级国产专播品爱网| 国产真实乱偷精品视频| 欧美在线一二三四区| 人妻少妇精品无码专区久久| 中文字幕日韩在线播放| 国产夫妻在线| 91九色露脸| 欧美成人自拍| 日韩一级在线免费观看| 床上的激情91.| 999精品视频在线观看播放| 同产精品九九九| 国产极品久久久| 色系列之999| 中文在线8资源库| 成人免费视频视频在| 久久神马影院| 日本精品久久久久中文字幕| 成人av电影在线观看| 91麻豆免费视频网站| 欧美视频在线一区| 瑟瑟在线观看| 91高清视频在线免费观看| 久久99成人| 一区二区三区久久网| 久久经典综合| 国产精品亚洲无码| 五月天一区二区三区| www日本高清视频| 久久久精品日本| 国产成人精品一区二区三区免费| 九九久久99| 99精品欧美| 国产综合内射日韩久| 一区二区国产视频| 国产成人精品免费看视频| 日韩色av导航| 高清在线一区二区| 曰韩不卡视频| 国产一区在线精品| 日韩精品一区二区亚洲av性色 | 91sa在线看| 日韩欧美在线精品| 亚洲欧洲日产国码无码久久99 | 日韩精品一区二区三区在线播放 | 国产一区二区三区四区二区| 日本免费黄视频| 久久久久综合网| 成人a v视频| 在线电影中文日韩| 高清在线一区| 小说区视频区图片区| 国产资源在线一区| avtt天堂在线| 精品噜噜噜噜久久久久久久久试看| 欧美78videosex性欧美| y111111国产精品久久婷婷| 在线欧美亚洲| 蜜桃传媒一区二区亚洲av| 91久久精品一区二区二区| 第一页在线观看| 成人免费福利视频| 国产精品草草| 日本japanese极品少妇| 色综合久久99| 麻豆传媒在线免费| 国产成人一区二区三区免费看| 亚洲黄色大片| 四虎永久免费影院| 欧美少妇xxx| 18视频在线观看| 好吊色欧美一区二区三区视频| 性伦欧美刺激片在线观看| 在线观看免费小视频| 91精品国产综合久久精品| 欧美1234区| 欧美一区二区三区在线免费观看| 日本亚洲欧美天堂免费| 精品欧美一区二区久久久久| 日韩av影院在线观看| 九色成人搞黄网站| 久久www视频| 国产日韩一级二级三级| 国产成人麻豆精品午夜在线| 久久噜噜噜精品国产亚洲综合| 奇米亚洲欧美| xxxxwww一片| 欧美中文一区二区三区| 久草在线视频资源| 日本午夜精品一区二区| 国产高清成人在线| 欧美黑人一区二区| 久久久99久久精品女同性| 欧美人与动xxxxz0oz| 成人性生交免费看| 香蕉加勒比综合久久| 在线日本中文字幕| 国产日韩欧美精品| 精品亚洲porn| www.com亚洲| 97国产精品视频| **女人18毛片一区二区| 亚洲精品视频久久久|