面試(Java之IO與NIO)

来源:https://www.cnblogs.com/kaikai-2018/archive/2019/05/15/10864061.html
-Advertisement-
Play Games

一、概念 NIO即New IO,這個庫是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實現方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多。在Java API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網路編程NIO。 二、NIO和IO的主要區別 下表總結 ...


一、概念

     NIO即New IO,這個庫是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但實現方式不同,NIO主要用到的是塊,所以NIO的效率要比IO高很多。在Java API中提供了兩套NIO,一套是針對標準輸入輸出NIO,另一套就是網路編程NIO。

二、NIO和IO的主要區別

下表總結了Java IO和NIO之間的主要區別:

IO

NIO

面向流

面向緩衝

阻塞IO

非阻塞IO

選擇器

 

 

 

 

 

 

 

 

1、面向流與面向緩衝

      Java IO和NIO之間第一個最大的區別是,IO是面向流的,NIO是面向緩衝區的。

Java IO面向流意味著每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據,需要先將它緩存到一個緩衝區。

    Java NIO面向緩衝區的緩衝導向方法略有不同。數據讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩衝區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區里尚未處理的數據。

2、阻塞與非阻塞IO

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

Java NIO的非阻塞模式,使一個線程從某通道發送請求讀取數據,但是它僅能得到目前可用的數據,如果目前沒有數據可用時,就什麼都不會獲取,而不是保持線程阻塞,所以直至數據變的可以讀取之前,該線程可以繼續做其他的事情。 非阻塞寫也是如此。一個線程請求寫入一些數據到某通道,但不需要等待它完全寫入,這個線程同時可以去做別的事情。 線程通常將非阻塞IO的空閑時間用於在其它通道上執行IO操作,所以一個單獨的線程現在可以管理多個輸入和輸出通道(channel)。

3、選擇器(Selectors)

     Java NIO的選擇器允許一個單獨的線程來監視多個輸入通道,你可以註冊多個通道使用一個選擇器,然後使用一個單獨的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個單獨的線程很容易來管理多個通道。

三、NIO和IO如何影響應用程式的設計

無論您選擇IO或NIO工具箱,可能會影響您應用程式設計的以下幾個方面:

1.對NIO或IO類的API調用。
2.數據處理。
3.用來處理數據的線程數。

1、API調用

當然,使用NIO的API調用時看起來與使用IO時有所不同,但這並不意外,因為並不是僅從一個InputStream逐位元組讀取,而是數據必須先讀入緩衝區再處理。

2、數據處理

使用純粹的NIO設計相較IO設計,數據處理也受到影響。

在IO設計中,我們從InputStream或 Reader逐位元組讀取數據。假設你正在處理一基於行的文本數據流,例如:

Name: Anna

Age: 25

Email: [email protected]

Phone: 1234567890

該文本行的流可以這樣處理:

InputStream input = ... ; // get the InputStream from the client socket  

 

BufferedReader reader = new BufferedReader(new InputStreamReader(input));  

 

String nameLine   = reader.readLine();

String ageLine    = reader.readLine();

String emailLine  = reader.readLine();

String phoneLine  = reader.readLine();

 

 

     請註意處理狀態由程式執行多久決定。換句話說,一旦reader.readLine()方法返回,你就知道肯定文本行就已讀完, readline()阻塞直到整行讀完,這就是原因。你也知道此行包含名稱;同樣,第二個readline()調用返回的時候,你知道這行包含年齡等。 正如你可以看到,該處理程式僅在有新數據讀入時運行,並知道每步的數據是什麼。一旦正在運行的線程已處理過讀入的某些數據,該線程不會再回退數據(大多如此)。下圖也說明瞭這條原則:

 

而一個NIO的實現會有所不同,下麵是一個簡單的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buffer);

註意第二行,從通道讀取位元組到ByteBuffer。當這個方法調用返回時,你不知道你所需的所有數據是否在緩衝區內。你所知道的是,該緩衝區包含一些位元組,這使得處理有點困難。假設第一次 read(buffer)調用後,讀入緩衝區的數據只有半行,例如,“Name:An”,你能處理數據嗎?顯然不能,需要等待,直到整行數據讀入緩存,在此之前,對數據的任何處理毫無意義。所以,你怎麼知道是否該緩衝區包含足夠的數據可以處理呢?好了,你不知道。發現的方法只能查看緩衝區中的數據。其結果是,在你知道所有數據都在緩衝區里之前,你必須檢查幾次緩衝區的數據。這不僅效率低下,而且可以使程式設計方案雜亂不堪。例如

ByteBuffer buffer = ByteBuffer.allocate(48);  

 

int bytesRead = inChannel.read(buffer);  

 

while(! bufferFull(bytesRead) ) {  

       bytesRead = inChannel.read(buffer);  

}

 

bufferFull()方法必須跟蹤有多少數據讀入緩衝區,並返回真或假,這取決於緩衝區是否已滿。換句話說,如果緩衝區準備好被處理,那麼表示緩衝區滿了。

bufferFull()方法掃描緩衝區,但必須保持在bufferFull()方法被調用之前狀態相同。如果沒有,下一個讀入緩衝區的數據可能無法讀到正確的位置。這是不可能的,但卻是需要註意的又一問題。

如果緩衝區已滿,它可以被處理。如果它不滿,並且在你的實際案例中有意義,你或許能處理其中的部分數據。但是許多情況下並非如此。下圖展示了“緩衝區數據迴圈就緒”:

 

四、IO與NIO的選擇

NIO可讓您只使用一個(或幾個)單線程管理多個通道(網路連接或文件),但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更複雜。

如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的數據,例如聊天伺服器,實現NIO的伺服器可能是一個優勢。同樣,如果你需要維持許多打開的連接到其他電腦上,如P2P網路中,使用一個單獨的線程來管理你所有出站連接,可能是一個優勢。一個線程多個連接的設計方案如下圖所示:

 

Java NIO: 單線程管理多個連接

如果你有少量的連接使用非常高的帶寬,一次發送大量的數據,也許典型的IO伺服器實現可能非常契合。下圖說明瞭一個典型的IO伺服器設計:

 

Java IO: 一個典型的IO伺服器設計- 一個連接通過一個線程處理.

PS.

1.   Java IO 流中涉及到了哪些設計策略和設計模式

Java 的 IO 庫提供了一種鏈接(Chaining)機制,可以將一個流處理器跟另一個流處理器首尾相接,以其中之一的輸出作為另一個的輸入而形成一個流管道鏈接,譬如常見的 new DataInputStream(new FileInputStream(file)) 就是把 FileInputStream 流當作 DataInputStream 流的管道鏈接。其次,對於 Java IO 流還涉及一種對稱性的設計策略,其表現為輸入輸出對稱性(如 InputStream 和 OutputStream 的位元組輸入輸出操作,Reader 和 Writer 的字元輸入輸出操作)和位元組字元的對稱性(InputStream 和 Reader 的位元組字元輸入操作,OutputStream 和 Writer 的位元組字元輸出操作)。此外,對於 Java IO 流在整體設計上還涉及裝飾者(Decorator)和適配器(Adapter)兩種設計模式。

對於 IO 流涉及的裝飾者設計模式例子如下:

//把InputStreamReader裝飾成BufferedReader來成為具備緩衝能力的Reader。BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

對於 IO 流涉及的適配器設計模式例子如下:

//把FileInputStream文件位元組流適配成InputStreamReader字元流來操作文件字元串。

FileInputStream fileInput = new FileInputStream(file);

InputStreamReader inputStreamReader = new InputStreamReader(fileInput);

而對於上面涉及的兩種設計模式通俗總結如下。裝飾者模式就是給一個對象增加一些新的功能,而且是動態的,要求裝飾對象和被裝飾對象實現同一個介面,裝飾對象持有被裝飾對象的實例(各種字元流間裝飾,各種位元組流間裝飾)。適配器模式就是將某個類的介面轉換成我們期望的另一個介面表示,目的是消除由於介面不匹配所造成的類的相容性問題(字元流與位元組流間互相適配)。

 

 

2.   位元組流與字元流有什麼區別:

電腦中的一切最終都是以二進位位元組形式存在的,對於我們經常操作的字元串,在寫入時其實都是先得到了其對應的位元組,然後將位元組寫入到輸出流,在讀取時其實都是先讀到的是位元組,然後將位元組直接使用或者轉換為字元給我們使用。由於對於位元組和字元兩種操作的需求比較廣泛,所以 Java 專門提供了字元流與位元組流相關IO類。對於程式運行的底層設備來說永遠都只接受位元組數據,所以當我們往設備寫數據時無論是位元組還是字元最終都是寫的位元組流。字元流是位元組流的包裝類,所以當我們將字元流向位元組流轉換時要註意編碼問題(因為字元串轉成位元組數組的實質是轉成該字元串的某種位元組編碼)。字元流和位元組流的使用非常相似,但是實際上位元組流的操作不會經過緩衝區(記憶體)而是直接操作文本本身的,而字元流的操作會先經過緩衝區(記憶體)然後通過緩衝區再操作文件。

字元流和位元組流的使用非常相似,但是實際上位元組流的操作不會經過緩衝區(記憶體)而是直接操作文本本身的,而字元流的操作會先經過緩衝區(記憶體)然後通過緩衝區再操作文件。

3.  位元組流和字元流哪個好,如何選擇?

緩大多數情況下使用位元組流會更好,因為位元組流是字元流的包裝,而大多數時候 IO 操作都是直接操作磁碟文件,所以這些流在傳輸時都是以位元組的方式進行的(圖片等都是按位元組存儲的)。

而如果對於操作需要通過 IO 在記憶體中頻繁處理字元串的情況使用字元流會好些,因為字元流具備緩衝區,提高了性能。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 狀態模式就是將狀態的條件判斷語句轉化成其函數重寫形式,利用了面向對象語言的多態性,本文根據https://blog.csdn.net/lm324114/article/details/78819602的情景將狀態模式用Matlab語言實現。 根據上圖情景,用傳統的方法實現如下: RoomState. ...
  • 本博文僅僅是筆者自己的學習路線,歸納整理了一些好的設計模式資料。 1、策略模式 參考資料: "c 設計模式 策略模式"   "c 設計模式之策略模式"   "C 設計模式(20)——策略者模式(Stragety Pattern)" 策略模式主要是將產品共有的部分抽象出來,不同的行 ...
  • 背景 ENode是一個CQRS+Event Sourcing架構的開發框架,Event Sourcing需要持久化事件,事件可以持久化在DB,但是DB由於面向的是CRUD場景,是針對數據會不斷修改或刪除的場景,所以內部實現會比較複雜,性能也相對比較低。而Event Store實際上對數據只有新增和查 ...
  • 兩台宿主機 一臺:作為伺服器 另一臺:作為客戶端 伺服器的步驟: 打開【控制面板】 點擊【程式】 點擊程式和功能中【打開或關閉Windows功能】 點擊【Telnet伺服器】--點擊【確定】 右擊【電腦】 點擊【管理】 將【Telnet】啟動類型改為自動(延遲啟動) 點擊【應用】 點擊【啟動】 在 ...
  • 不少Python新手經常問到學Python到底需不需要學習Linux? Python不是支持Windows和Linux操作系統嗎?能在Windows下開發為什麼還要學習Linux? ...
  • 對於應屆生來說,Java基礎和項目哪個更重要?這可能是眾多困惑中關心的人數比較多的一個。主要關心這個問題的有兩種 ...
  • 在本教程中,我們將討論java中的do-while迴圈。do-while迴圈類似於while迴圈,但是它們之間有一個區別:在while迴圈中,迴圈條件在迴圈的主體執行之前進行評估,而在do-while迴圈中,迴圈條件在迴圈的主體執行之後再進行評估。 ...
  • 第1題:列表[1,2,3,4,5],請使用map()函數輸出[1,4,9,16,25],並使用列表推導式提取出大於10的數,最終輸出[16,25]。 map是python高階用法,字面意義是映射,它的作用就是把一個數據結構映射成另外一種數據結構。 map用法比較繞,最好是對基礎數據結構很熟悉了再使用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...