NIO 非阻塞式IO

NIO

Java NIO 基本介紹

  1. Java NIO 全稱 Java non-blocking IO,是指 JDK 提供的新 API。從 JDK1.4 開始,Java 提供了一系列改進的輸入/輸出的新特性,被統稱為 NIO(即 NewIO),是同步非阻塞的。
  2. NIO 相關類都被放在 java.nio 包及子包下,并且對原 java.io 包中的很多類進行改寫。
  3. NIO 有三大核心部分:Channel(通道)、Buffer(緩沖區)、Selector(選擇器)
  4. NIO面向緩沖區,或者面向塊編程的。數據讀取到一個它稍后處理的緩沖區,需要時可在緩沖區中前后移動,這就增加了處理過程中的靈活性,使用它可以提供非阻塞式的高伸縮性網絡。
  5. Java NIO 的非阻塞模式,使一個線程從某通道發送請求或者讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什么都不會獲取,而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。非阻塞寫也是如此,一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。
  6. 通俗理解:NIO 是可以做到用一個線程來處理多個操作的。假設有 10000 個請求過來,根據實際情況,可以分配 50 或者 100 個線程來處理。不像之前的阻塞 IO 那樣,非得分配 10000 個。
  7. HTTP 2.0 使用了多路復用的技術,做到同一個連接并發處理多個請求,而且并發請求的數量比 HTTP 1.1 大了好幾個數量級。

NIO 三大核心原理示意圖

一張圖描述 NIOSelectorChannelBuffer 的關系。

  1. 每個 Channel 都會對應一個 Buffer
  2. Selector 對應一個線程,一個線程對應多個 Channel(連接)。
  3. 該圖反應了有三個 Channel 注冊到該 Selector //程序
  4. 程序切換到哪個 Channel 是由事件決定的,Event 就是一個重要的概念。
  5. Selector 會根據不同的事件,在各個通道上切換。
  6. Buffer 就是一個內存塊,底層是有一個數組。
  7. 數據的讀取寫入是通過 Buffer,這個和 BIO是不同的,BIO 中要么是輸入流,或者是輸出流,不能雙向,但是 NIO 的 Buffer 是可以讀也可以寫,需要 flip 方法切換 Channel 是雙向的,可以返回底層操作系統的情況,比如 Linux,底層的操作系統通道就是雙向的。在這里插入圖片描述

NIO 和 BIO 的比較

  1. BIO 以流的方式處理數據,而 NIO 以塊的方式處理數據,塊 I/O 的效率比流 I/O 高很多。

  2. BIO 是阻塞的,NIO 則是非阻塞的。

  3. BIO 基于字節流和字符流進行操作,而 NIO 基于 Channel(通道)和 Buffer(緩沖區)進行操作,數據總是從通道讀取到緩沖區中,或者從緩沖區寫入到通道中。Selector(選擇器)用于監聽多個通道的事件(比如:連接請求,數據到達等),因此使用單個線程就可以監聽多個客戶端通道。

  4. Buffer和Channel之間的數據流向是雙向的

緩沖區(Buffer)

緩沖區(Buffer):緩沖區本質上是一個可以讀寫數據的內存塊,可以理解成是一個**容器對象(含數組)**該對象提供了一組方法,可以更輕松地使用內存塊,,緩沖區對象內置了一些機制,能夠跟蹤和記錄緩沖區的狀態變化情況。Channel 提供從文件、網絡讀取數據的渠道,但是讀取或寫入的數據都必須經由 Buffer

通道(Channel)

NIO 的通道類似于流,但有些區別如下:

  • 通道可以同時進行讀寫,而流只能讀或者只能寫
  • 通道可以實現異步讀寫數據
  • 通道可以從緩沖讀數據,也可以寫數據到緩沖:
  1. BIO 中的 Stream 是單向的,例如 FileInputStream 對象只能進行讀取數據的操作,而 NIO 中的通道(Channel)是雙向的,可以讀操作,也可以寫操作。
  2. ChannelNIO 中是一個接口 public interface Channel extends Closeable{}
  3. 常用的 Channel 類有:FileChannelDatagramChannelServerSocketChannelSocketChannel。【ServerSocketChanne 類似 ServerSocketSocketChannel 類似 Socket
  4. FileChannel 用于文件的數據讀寫,DatagramChannel 用于 UDP 的數據讀寫,ServerSocketChannelSocketChannel 用于 TCP 的數據讀寫。

NIO 還支持通過多個 Buffer(即 Buffer數組)完成讀寫操作,即 ScatteringGathering【舉例說明】

/*** @Author:jiangdw7* @date: 2023/8/9 10:04*/
public class ScatteringAndGatheringTest {public static void main(String[] args) throws Exception {//使用 ServerSocketChannel 和 SocketChannel 網絡ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);//綁定端口到 socket,并啟動serverSocketChannel.socket().bind(inetSocketAddress);//創建 buffer 數組ByteBuffer[] byteBuffers = new ByteBuffer[2];byteBuffers[0] = ByteBuffer.allocate(5);byteBuffers[1] = ByteBuffer.allocate(3);//等客戶端連接 (telnet)SocketChannel socketChannel = serverSocketChannel.accept();int messageLength = 8; //假定從客戶端接收 8 個字節//循環的讀取while (true) {int byteRead = 0;while (byteRead < messageLength) {long l = socketChannel.read(byteBuffers);byteRead += l; //累計讀取的字節數System.out.println("byteRead = " + byteRead);//使用流打印,看看當前的這個 buffer 的 position 和 limitArrays.asList(byteBuffers).stream().map(buffer -> "position = " + buffer.position() + ", limit = " + buffer.limit()).forEach(System.out::println);}//將所有的 buffer 進行 flipArrays.asList(byteBuffers).forEach(buffer -> buffer.flip());//將數據讀出顯示到客戶端long byteWirte = 0;while (byteWirte < messageLength) {long l = socketChannel.write(byteBuffers);byteWirte += l;}//將所有的buffer進行clearArrays.asList(byteBuffers).forEach(buffer -> {buffer.clear();});System.out.println("byteRead = " + byteRead + ", byteWrite = " + byteWirte + ", messagelength = " + messageLength);}}
}

Selector(選擇器)

  1. JavaNIO,用非阻塞的 IO 方式。可以用一個線程,處理多個的客戶端連接,就會使用到 Selector(選擇器)。
  2. Selector 能夠檢測多個注冊的通道上是否有事件發生(注意:多個 Channel 以事件的方式可以注冊到同一個 Selector),如果有事件發生,便獲取事件然后針對每個事件進行相應的處理。這樣就可以只用一個單線程去管理多個通道,也就是管理多個連接和請求。
  3. 只有在連接/通道真正有讀寫事件發生時,才會進行讀寫,就大大地減少了系統開銷,并且不必為每個連接都創建一個線程,不用去維護多個線程。
  4. 避免了多線程之間的上下文切換導致的開銷。

注意事項

  1. NIO 中的 ServerSocketChannel 功能類似 ServerSocketSocketChannel 功能類似 Socket
  2. Selector 相關方法說明
    • selector.select(); //阻塞
    • selector.select(1000); //阻塞 1000 毫秒,在 1000 毫秒后返回
    • selector.wakeup(); //喚醒 selector
    • selector.selectNow(); //不阻塞,立馬返還
public class NIOClient {private static Selector selector;public static void main(String[] args) throws Exception {selector = Selector.open();SocketChannel sc = SocketChannel.open();sc.configureBlocking(false);sc.connect(new InetSocketAddress("127.0.0.1", 8081));sc.register(selector, SelectionKey.OP_READ);ByteBuffer bf = ByteBuffer.allocate(1024);bf.put("Hi,server,i'm client".getBytes());if (sc.finishConnect()) {bf.flip();while (bf.hasRemaining()) {sc.write(bf);}while (sc.isConnected()) {selector.select();Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey key = it.next();if (key.isReadable()) {ByteArrayOutputStream bos = new ByteArrayOutputStream();bf.clear();SocketChannel othersc = (SocketChannel) key.channel();while (othersc.read(bf) > 0) {bf.flip();while (bf.hasRemaining()) {bos.write(bf.get());}bf.clear();}System.out.println("服務端返回的數據:" + bos.toString());Thread.sleep(5000);sc.close();System.out.println("客戶端關閉...");}}selector.selectedKeys().clear();}}}
}
public class NIOServer {private static Selector selector;private static ServerSocketChannel serverSocketChannel;private static ByteBuffer bf = ByteBuffer.allocate(1024);public static void main(String[] args) throws Exception {init();while (true) {int select = selector.select(10000);if (select == 0) {System.out.println("等待連接10秒...");continue;}Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()) {SelectionKey key = it.next();if (key.isAcceptable()) {System.out.println("連接準備就緒");ServerSocketChannel server = (ServerSocketChannel) key.channel();System.out.println("等待客戶端連接中........................");SocketChannel channel = server.accept();channel.configureBlocking(false);channel.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {System.out.println("讀準備就緒,開始讀.......................");SocketChannel channel = (SocketChannel) key.channel();System.out.println("客戶端的數據如下:");int readLen = 0;bf.clear();StringBuffer sb = new StringBuffer();while ((readLen = channel.read(bf)) > 0) {bf.flip();byte[] temp = new byte[readLen];bf.get(temp, 0, readLen);sb.append(new String(temp));bf.clear();}if (-1 == readLen) {System.out.println(channel.hashCode()+"號客戶端關閉。");channel.close();}else {channel.write(ByteBuffer.wrap(("客戶端,你傳過來的數據是:" + sb.toString()).getBytes()));System.out.println(sb.toString()+"132123");}}it.remove();}}}private static void init() throws Exception {selector = Selector.open();serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.socket().bind(new InetSocketAddress(8081));serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);}
}

參考連接

https://www.cnblogs.com/xdouby/p/8942083.html

https://www.zhihu.com/question/22524908

https://blog.csdn.net/ArtAndLife/article/details/121001656

AIO

  1. JDK7 引入了 AsynchronousI/O,即 AIO。在進行 I/O 編程中,常用到兩種模式:ReactorProactorJavaNIO 就是 Reactor,當有事件觸發時,服務器端得到通知,進行相應的處理

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

  3. 目前 AIO 還沒有廣泛應用,Netty 也是基于 NIO,而不是 AIO,因此我們就不詳解 AIO 了,有興趣的同學可以參考《Java新一代網絡編程模型AIO原理及Linux系統AIO介紹》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/36519.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/36519.shtml
英文地址,請注明出處:http://en.pswp.cn/news/36519.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

01-Spark環境部署

1 Spark的部署方式介紹 ? Spark部署模式分為Local模式&#xff08;本地模式&#xff09;和集群模式&#xff08;集群模式又分為Standalone模式、Yarn模式和Mesos模式&#xff09; 1.1 Local模式 Local模式常用于本地開發程序與測試&#xff0c;如在idea中 1.2 Standalone模…

PAT 1039 Course List for Student

個人學習記錄&#xff0c;代碼難免不盡人意。 Zhejiang University has 40000 students and provides 2500 courses. Now given the student name lists of all the courses, you are supposed to output the registered course list for each student who comes for a query. …

netty學習分享 二

操作系統IO模型與實現原理 阻塞IO 模型 應用程序調用一個IO函數&#xff0c;導致應用程序阻塞&#xff0c;等待數據準備好。如果數據沒有準備好&#xff0c;一直等待….數據準備好了&#xff0c;從內核拷貝到用戶空間,IO函數返回成功指示。 當調用recv()函數時&#xff0c;系…

釉面陶瓷器皿SOR/2016-175標準上架亞馬遜加拿大站

親愛的釉面陶瓷器皿和玻璃器皿制造商和賣家&#xff0c;亞馬遜加拿大站將執行SOR/2016-175法規。這是一份新的法規&#xff0c;規定了含有鉛和鎘的釉面陶瓷器和玻璃器皿需要滿足的要求。讓我們一起來看一看&#xff0c;為什么要實行SOR/2016-175法規&#xff1f;這是一個保護消…

yolo源碼注釋3——模型配置文件

代碼基于yolov5 v6.0 目錄&#xff1a; yolo源碼注釋1——文件結構yolo源碼注釋2——數據集配置文件yolo源碼注釋3——模型配置文件yolo源碼注釋4——yolo-py 模型配置文件一般放在 models 文件夾下的 XXX.yaml 文件中&#xff0c;以 yolov5s.yaml 為例&#xff1a; # YOLOv…

使用SpringAop切面編程通過Spel表達式實現Controller權限控制

目錄 參考一、概念SpEL表達式 二、開發引入包定義注解定義切面定義用戶上下文 三、測試新建Service在方法上注解新建Service在類上注解運行 參考 SpringBoot&#xff1a;SpEL讓復雜權限控制變得很簡單 一、概念 對于在Springboot中&#xff0c;利用自定義注解切面來實現接口…

opencv實戰項目 手勢識別-手勢音量控制(opencv)

本項目是使用了谷歌開源的框架mediapipe&#xff0c;里面有非常多的模型提供給我們使用&#xff0c;例如面部檢測&#xff0c;身體檢測&#xff0c;手部檢測等。 手勢識別系列文章 1.opencv實現手部追蹤&#xff08;定位手部關鍵點&#xff09; 2.opencv實戰項目 實現手勢跟蹤…

8月14日,每日信息差

1、FF正式交付首輛FF 91 2.0 Futurist Alliance給塔尖用戶 2、消息稱iPhone SE 4設計基于iPhone 14&#xff0c;但仍是后置單攝像頭 3、阿聯酋力推電動汽車發展。該政策將作為一個監管框架&#xff0c;明確電動汽車充電站等基礎設施建設的標準&#xff0c;并推動全國標準統一…

Jay17 2023.8.12日報

8.12 今天做了2題&#xff0c;CTFshow 紅包挑戰8&#xff08;PHP create_function()&#xff09;和BUU [RoarCTF 2019]Easy Java&#xff08;web.xml泄露&#xff09;。 此外一直在打NepCTF&#xff0c;出了一題&#xff08;ez_java_checkin&#xff09;簡單了解了java中shri…

Kafka消息隊列學習(一)

文章目錄 概述核心概念生產者示例同步 / 異步發送消息生產者參數配置ack-確認機制retries - 重試次數compression_type - 消息壓縮類型 分區機制分區策略 消費者消息有序性提交和偏移量偏移量提交方式手動提交 高可用設計 SpringBoot集成Kafka基本使用傳遞對象消息 概述 核心概…

HTTP之cookie基礎學習

目錄 Cookie 什么是Cookie Cookie分類 Cookie版本 Cookie工作原理 Cookie詳解 創建cookie cookie編碼 cookie過期時間選項 Cookie流程 Cookie使用 會話管理 個性化信息 記錄用戶的行為 Cookie屬性 domain選項 path選項 secure選項 cookie…

帶著問題學習分布式系統

寫在前面 聽過很多道理&#xff0c;卻依然過不好這一生。 看過很多關于學習的技巧、方法&#xff0c;卻沒應用到自己的學習中。 隨著年紀變大&#xff0c;記憶力越來越差&#xff0c;整塊的時間也越來越少&#xff0c;于是&#xff0c;越來越希望能夠更高效的學習。學習是一種習…

香港大學余濤組推出開源XLANG Agent!支持三種Agent模式

作者 |小戲、ZenMoore 一個新的未來又逐漸開始從理論走向現實走到我們身邊了。 語言的意義在于使用&#xff0c;而從 ChatGPT 以來這些大規模語言模型的意義&#xff0c;也必然絕不止于 Chat&#xff0c;在四個月前&#xff0c;我們介紹了清華大學關于工具學習的綜述《清華發布…

Python-OpenCV中的圖像處理-圖像特征

Python-OpenCV中的圖像處理-圖像特征 圖像特征Harris角點檢測亞像素級精度的角點檢測Shi-Tomasi角點檢測SIFT(Scale-Invariant Feature Transfrom)SURF(Speeded-Up Robust Features) 圖像特征 特征理解特征檢測特征描述 Harris角點檢測 cv2.cornerHarris(img, blockSize, ks…

海格里斯HEGERLS四向穿梭車倉儲解決方案在電子商務行業中的應用

隨著現代物流&#xff0c;尤其是智能化物流的飛速發展&#xff0c;河北沃克金屬制品有限公司看到了智能物流領域背后的巨大價值和市場空間&#xff0c;深知物流與供應鏈對企業發展的重要性。于是&#xff0c;引進了先進的高科技智能技術—HEGERLS四向穿梭車技術&#xff0c;并迅…

【日常積累】Linux下文件亂碼解決

linux下刪除亂碼文件、目錄 由于編碼原因&#xff0c;在linux服務器上上傳、創建中文文件或目錄時&#xff0c;會產生亂碼&#xff0c;如果想刪除它&#xff0c;有時候發現用rm命令是刪除不了的 這種情況下&#xff0c;用find命令可以刪除亂碼的文件或目錄。 首先進入亂碼文件…

docker 網絡訪問診斷

本地docker開啟nginx服務等&#xff0c; 發現linux系統重啟之后&#xff0c;無法訪問&#xff0c; 進入容器內部&#xff0c;發現可以訪問 但是容器外部&#xff0c;映射端口無法訪問&#xff1b; 診斷之前&#xff0c;發現docker0沒有IP綁定 rootbook:/etc/docker# ip addr …

自制手寫機器人

寫字機器人模擬在畫圖板上寫字效果 寫了一套寫字機器人代碼&#xff0c;有多種字體可供選擇&#xff0c;需要的朋友私信獲取代碼和軟件

Spring5學習筆記— 工廠高級特性

?作者簡介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;熱愛Java后端開發者&#xff0c;一個想要與大家共同進步的男人&#x1f609;&#x1f609; &#x1f34e;個人主頁&#xff1a;Leo的博客 &#x1f49e;當前專欄&#xff1a; Spring專欄 ?特色專欄&#xff1a; M…

創建型模式-原型模式

文章目錄 一、原型模式1. 概述2. 結構3. 實現4. 案例1.5 使用場景1.6 擴展&#xff08;深克隆&#xff09; 一、原型模式 1. 概述 用一個已經創建的實例作為原型&#xff0c;通過復制該原型對象來創建一個和原型對象相同的新對象。 2. 結構 原型模式包含如下角色&#xff1a; …