2020 最新IO與NIO面試題

2020 最新IO與NIO面試題

不多說廢話,直接分享!

1、Java中IO 流?

Java IO 流分爲幾種?

  1. 按照流的流向分,可以分爲輸入流和輸出流;
  2. 按照操作單元劃分,可以劃分爲字節流和字符流;
  3. 按照流的角色劃分爲節點流和處理流。

Java Io 流共涉及 40 多個類,這些類看上去很雜亂,但實際上很有規則,而且彼此之間存在非常緊密的

聯繫, Java I0 流的 40 多個類都是從如下 4 個抽象類基類中派生出來的。

  1. InputStream/Reader: 所有的輸入流的基類,前者是字節輸入流,後者是字符輸入流。
  2. OutputStream/Writer: 所有輸出流的基類,前者是字節輸出流,後者是字符輸出流。

2、 Java IO與 NIO的區別

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

3、常用io類有那些

File
FileInputSteam,FileOutputStream
BufferInputStream,BufferedOutputSream
PrintWrite
FileReader,FileWriter
BufferReader,BufferedWriter
ObjectInputStream,ObjectOutputSream

4、字節流與字符流的區別

以字節爲單位輸入輸出數據,字節流按照8位傳輸 以字符爲單位輸入輸出數據,字符流按照16位傳輸

5、阻塞 IO 模型

最傳統的一種 IO 模型,即在讀寫數據過程中會發生阻塞現象。當用戶線程發出 IO 請求之後,內核會去查看數據是否就緒,如果沒有就緒就會等待數據就緒,而用戶線程就會處於阻塞狀態,用戶線程交出 CPU。當數據就緒之後,內核會將數據拷貝到用戶線程,並返回結果給用戶線程,用 戶線程才解除 block 狀態。典型的阻塞 IO 模型的例子爲: data = socket.read();如果數據沒有就緒,就會一直阻塞在 read 方法

6、非阻塞 IO 模型

當用戶線程發起一個 read 操作後,並不需要等待,而是馬上就得到了一個結果。 如果結果是一個error 時,它就知道數據還沒有準備好,於是它可以再次發送 read 操作。一旦內核中的數據準備好了,並且又再次收到了用戶線程的請求,那麼它馬上就將數據拷貝到了用戶線程,然後返回。所以事實上,在非阻塞 IO 模型中,用戶線程需要不斷地詢問內核數據是否就緒,也就說非阻塞 IO不會交出 CPU,而會一直佔用 CPU。 典型的非阻塞 IO 模型一般如下:

while(true){
    data = socket.read();
    if(data!= error){
        //處理數據
        break;
    }
}

但是對於非阻塞 IO 就有一個非常嚴重的問題, 在 while 循環中需要不斷地去詢問內核數據是否就緒,這樣會導致 CPU 佔用率非常高,因此一般情況下很少使用 while 循環這種方式來讀取數據。

7、多路複用 IO 模型

多路複用 IO 模型是目前使用得比較多的模型。 Java NIO 實際上就是多路複用 IO。在多路複用 IO模型中,會有一個線程不斷去輪詢多個 socket 的狀態,只有當 socket 真正有讀寫事件時,才真正調用實際的 IO 讀寫操作。因爲在多路複用 IO 模型中,只需要使用一個線程就可以管理多個socket,系統不需要建立新的進程或者線程,也不必維護這些線程和進程,並且只有在真正有socket 讀寫事件進行時,纔會使用 IO 資源,所以它大大減少了資源佔用。在 Java NIO 中,是通過 selector.select()去查詢每個通道是否有到達事件,如果沒有事件,則一直阻塞在那裏,因此這種方式會導致用戶線程的阻塞。多路複用 IO 模式,通過一個線程就可以管理多個 socket,只有當 socket 真正有讀寫事件發生纔會佔用資源來進行實際的讀寫操作。因此,多路複用 IO 比較適合連接數比較多的情況。

另外多路複用 IO 爲何比非阻塞 IO 模型的效率高是因爲在非阻塞 IO 中,不斷地詢問 socket 狀態時通過用戶線程去進行的,而在多路複用 IO 中,輪詢每個 socket 狀態是內核在進行的,這個效率要比用戶線程要高的多。

不過要注意的是,多路複用 IO 模型是通過輪詢的方式來檢測是否有事件到達,並且對到達的事件 逐一進行響應。因此對於多路複用 IO 模型來說, 一旦事件響應體很大,那麼就會導致後續的事件 遲遲得不到處理,並且會影響新的事件輪詢。

8、信號驅動 IO 模型

在信號驅動 IO 模型中,當用戶線程發起一個 IO 請求操作,會給對應的 socket 註冊一個信號函數,然後用戶線程會繼續執行,當內核數據就緒時會發送一個信號給用戶線程,用戶線程接收到信號之後,便在信號函數中調用 IO 讀寫操作來進行實際的 IO 請求操作。

9、異步 IO 模型

異步 IO 模型纔是最理想的 IO 模型,在異步 IO 模型中,當用戶線程發起 read 操作之後,立刻就可以開始去做其它的事。而另一方面,從內核的角度,當它受到一個 asynchronous read 之後,它會立刻返回,說明 read 請求已經成功發起了,因此不會對用戶線程產生任何 block。然後,內核會等待數據準備完成,然後將數據拷貝到用戶線程,當這一切都完成之後,內核會給用戶線程發送一個信號,告訴它 read 操作完成了。也就說用戶線程完全不需要實際的整個 IO 操作是如何進行的, 只需要先發起一個請求,當接收內核返回的成功信號時表示 IO 操作已經完成,可以直接去使用數據了。

也就說在異步 IO 模型中, IO 操作的兩個階段都不會阻塞用戶線程,這兩個階段都是由內核自動完成,然後發送一個信號告知用戶線程操作已完成。用戶線程中不需要再次調用 IO 函數進行具體的讀寫。這點是和信號驅動模型有所不同的,在信號驅動模型中,當用戶線程接收到信號表示數據已經就緒,然後需要用戶線程調用 IO 函數進行實際的讀寫操作;而在異步 IO 模型中,收到信號表示 IO 操作已經完成,不需要再在用戶線程中調用 IO 函數進行實際的讀寫操作。

注意,異步 IO 是需要操作系統的底層支持,在 Java 7 中,提供了 Asynchronous IO。 更多參考: http://www.importnew.com/19816.html

10、JAVA NIO

NIO 主要有三大核心部分: Channel(通道), Buffer(緩衝區), Selector。傳統 IO 基於字節流和字符流進行操作, 而 NIO 基於 Channel 和 Buffer(緩衝區)進行操作,數據總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。 Selector(選擇區)用於監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個線程可以監聽多個數據通道。 NIO 和傳統 IO 之間第一個最大的區別是, IO 是面向流的, NIO 是面向緩衝區的。

11、NIO 的緩衝區

Java IO 面向流意味着每次從流中讀一個或多個字節,直至讀取所有字節,它們沒有被緩存在任何地方。此外,它不能前後移動流中的數據。如果需要前後移動從流中讀取的數據, 需要先將它緩存到一個緩衝區。 NIO 的緩衝導向方法不同。數據讀取到一個它稍後處理的緩衝區,需要時可在緩衝區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩衝區中包含所有您需要處理的數據。而且,需確保當更多的數據讀入緩衝區時,不要覆蓋緩衝區裏尚未處理的數據。

12、NIO 的非阻塞

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

13、Channel

首先說一下 Channel,國內大多翻譯成“通道”。 Channel 和 IO 中的 Stream(流)是差不多一個等級的。 只不過 Stream 是單向的,譬如: InputStream, OutputStream, 而 Channel 是雙向的,既可以用來進行讀操作,又可以用來進行寫操作。NIO 中的 Channel 的主要實現有:

  1. FileChannel
  2. DatagramChannel
  3. SocketChannel
  4. ServerSocketChannel 這裏看名字就可以猜出個所以然來:分別可以對應文件 IO、 UDP 和 TCP(Server 和 Client)。 下面演示的案例基本上就是圍繞這 4 個類型的 Channel 進行陳述的。

14、Buffer

Buffer,故名思意, 緩衝區,實際上是一個容器,是一個連續數組。 Channel 提供從文件、網絡讀取數據的渠道,但是讀取或寫入的數據都必須經由 Buffer。

img

上面的圖描述了從一個客戶端向服務端發送數據,然後服務端接收數據的過程。客戶端發送數據時,必須先將數據存入 Buffer 中,然後將 Buffer 中的內容寫入通道。服務端這邊接收數據必須通過 Channel 將數據讀入到 Buffer 中,然後再從 Buffer 中取出數據來處理。

在 NIO 中, Buffer 是一個頂層父類,它是一個抽象類,常用的 Buffer 的子類有:ByteBuffer、 IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、 FloatBuffer、ShortBuffer

15、Selector

Selector 類是 NIO 的核心類, Selector 能夠檢測多個註冊的通道上是否有事件發生,如果有事件發生,便獲取事件然後針對每個事件進行相應的響應處理。這樣一來,只是用一個單線程就可以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正有讀寫事件發生時,纔會調用函數來進行讀寫,就大大地減少了系統開銷,並且不必爲每個連接都創建一個線程,不用去維護多個線程,並且避免了多線程之間的上下文切換導致的開銷。

對每個事件進行相應的響應處理。這樣一來,只是用一個單線程就可以管理多個通道,也就是管理多個連接。這樣使得只有在連接真正有讀寫事件發生時,纔會調用函數來進行讀寫,就大大地減少了系統開銷,並且不必爲每個連接都創建一個線程,不用去維護多個線程,並且避免了多線程之間的上下文切換導致的開銷。

另外,還整理了一個整理了一本面試電子書,共 300頁!目錄如下


差不多花了大半個月在整理成冊,另外創建了一個比較大家關心的內推羣,自建立起就很受歡迎!羣裏目前有不少獵頭和各大互聯網公司HR,大多數來自,北京,上海,廣州,深圳,杭州,希望能給大家來帶幫助!

給俺點個讚唄,可以讓更多的人看到這篇文章,順便激勵下我,嘻嘻。

作者簡潔
作者:大家好,我是軍長,一如既往的爲廣大Java讀者奉獻MySQL、SSM、Redis、Spring,並且整理了300頁Java面試手冊個人博客 全部是Java,分佈式,大數據的系列文章。 轉載說明:未獲得授權,禁止轉載

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