Java NIO 簡介

引自:http://blog.iluckymeeting.com/2018/02/02/JavaNioOverview/

Java NIO簡介

Java 1.4開始引入了Java NIO以替換標(biāo)準(zhǔn)Java IO,標(biāo)準(zhǔn)Java IO是基于流(包括byte stream和character stream)的阻塞IO,而Java NIO是基于Channel和Buffer的非阻塞IO.

Java NIO中所有的IO操作都是基于Channel和Buffer的
Java NIO讀寫數(shù)據(jù)

Java NIO的所有IO操作都是非阻塞的,當(dāng)線程要讀取IO數(shù)據(jù)時(shí),Channel會(huì)把數(shù)據(jù)讀入Buffer,在讀入的過程中線程不用阻塞等待,可以去處理別的任務(wù),當(dāng)數(shù)據(jù)讀入完成后再回來繼續(xù)處理;同理,當(dāng)線程要寫入數(shù)據(jù)時(shí),Buffer中的數(shù)據(jù)被寫入Channel,在寫入的過程中線程不用阻塞。

Java NIO有三個(gè)核心構(gòu)件:

  • Channel
  • Buffer
  • Selector

Java NIO的所有IO操作都是通過Channel和Buffer完成的,IO讀取是數(shù)據(jù)由Channel讀入Buffer,IO寫入是數(shù)據(jù)由Buffer寫入Channel。Channel接口的繼承結(jié)構(gòu)如下:
Channel interface

Java NIO中的幾個(gè)核心Channel:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

Buffer是一個(gè)項(xiàng)部抽象類:
Buffer

Buffer支持全部的基本類型byte、char、double、float、int、long、short,對應(yīng)的提供了

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

一個(gè)線程要管理多個(gè)Channel連接就要依賴Selector,當(dāng)然這種情況下一般是有多個(gè)打開的連接,并且各連接要處理的IO事件都比較輕量化

Thread-Selector-Channel

Channel創(chuàng)建后會(huì)注冊到Selector上,Selector的select()方法調(diào)用后會(huì)阻塞,等待注冊的多個(gè)Channel有事件到達(dá),然后select()方法返回,線程繼續(xù)處理。

Java NIO與Java IO比較

Java IO Java NIO
基于Stream 基于Buffer
阻塞IO 非阻塞IO
Selector

基于Stream Vs. 基于Buffer

Java IO是基于Stream的,數(shù)據(jù)讀取操作是由Stream中讀取一個(gè)或多個(gè)byte,每個(gè)byte只能由Stream中讀取一次,如果要重復(fù)使用讀取的數(shù)據(jù)只能是將Stream讀出的數(shù)據(jù)緩存起來。

Java NIO是基于Buffer的,數(shù)據(jù)讀取操作會(huì)將Channel中的數(shù)據(jù)讀入Buffer,之后線程可以從任意位置開始使用Buffer里的數(shù)據(jù),并且可以重復(fù)讀取Buffer里的數(shù)據(jù),方便靈活。處理Buffer里的數(shù)據(jù)有兩個(gè)注意點(diǎn):

  • Buffer里的數(shù)據(jù)是否是全部數(shù)據(jù),也就是是否所有數(shù)據(jù)都已經(jīng)由Channel中讀入Buffer
  • Buffer是否已被填滿,如果Buffer已被填滿的情況下再往里寫入數(shù)據(jù),則之前寫入的數(shù)據(jù)會(huì)被覆蓋

阻塞IO Vs. 非阻塞IO

Java IO基于Stream的讀寫操作是阻塞的,發(fā)起讀寫操作后線程會(huì)阻塞直到讀寫完成;Java NIO基于Channel和Buffer的讀寫操作是非阻塞的,發(fā)起讀操作時(shí),數(shù)據(jù)會(huì)由Channel讀入Buffer,發(fā)起寫操作時(shí),數(shù)據(jù)會(huì)由Buffer寫入Channel,線程不必阻塞等待。

Selector

Java NIO之所以能做到非阻塞讀寫操作,是因?yàn)橛蠸elector的存在,當(dāng)Channel連接建立時(shí)會(huì)注冊到Selector上,線程通過Selector來操作Channel完成數(shù)據(jù)讀寫,Selector阻塞在select()方法上,當(dāng)有Channel達(dá)到就緒狀態(tài)時(shí),Selector會(huì)選取這個(gè)Channel進(jìn)行讀寫操作,這個(gè)過程中線程不必阻塞,可以同時(shí)去處理別的任務(wù)。

Java IO使用 Vs. Java NIO使用

假設(shè)要傳輸以下內(nèi)容:

Name:張三
Age:34
Addr:北京市

Java IO的處理是:

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 addrLine  = reader.readLine();

Java IO讀操作首先會(huì)阻塞在第一個(gè)reader.readLine()操作上,直到第一行數(shù)據(jù)讀取完成,接著阻塞到第二個(gè)reader.readLine()方法上,直到第二行數(shù)據(jù)讀取完成,接著阻塞在第三個(gè)reader.readLine()方法上,直到第三行數(shù)據(jù)讀取完成。由此可見Java IO操作雖然是阻塞的,但是可以明確的知道數(shù)據(jù)讀取的進(jìn)度和內(nèi)容,數(shù)據(jù)處理邏輯相對簡單。

Java NIO的處理是:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);
while(! bufferFull(bytesRead) ) {
    bytesRead = inChannel.read(buffer);
}

當(dāng)inChannel.read(buffer)被調(diào)用時(shí),只能確定Channel中有數(shù)據(jù)讀入了Buffer,但是并不知道是否所有數(shù)據(jù)都已經(jīng)讀入了Buffer中,只好不斷檢測bufferFull()方法,直到所有Buffer被填滿.
由此可見Java NIO的讀寫操作雖然不會(huì)阻塞線程,但是數(shù)據(jù)處理邏輯相對較復(fù)雜。

綜上所述,Java NIO的非阻塞特性可以讓一個(gè)線程輕松管理多個(gè)Channel連接,但是數(shù)據(jù)處理的代價(jià)較Java IO要更大,所以如果要管理大量連接并且每個(gè)連接要處理的數(shù)據(jù)量不大,則可以選擇Java NIO;如果連接數(shù)量不多,但是每個(gè)連接占用的帶寬很大,則可以選擇Java IO。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容