java_IO流之 NIO
NIO 定義
即新IO,在JDK1.4的java.nio.*包中引入,其目的在于提高速度。
在Java1.4之前的I/O系統(tǒng)中,提供的都是面向流的I/O系統(tǒng),系統(tǒng)一次一個(gè)字節(jié)地處理數(shù)據(jù),一個(gè)輸入流產(chǎn)生一個(gè)字節(jié)的數(shù)據(jù),一個(gè)輸出流消費(fèi)一個(gè)字節(jié)的數(shù)據(jù),面向流的I/O速度非常慢,而在Java 1.4中推出了NIO,這是一個(gè)面向塊的I/O系統(tǒng),系統(tǒng)以塊的方式處理處理,每一個(gè)操作在一步中產(chǎn)生或者消費(fèi)一個(gè)數(shù)據(jù)庫(kù),按塊處理要比按字節(jié)處理數(shù)據(jù)快的多。
速度的提高來(lái)自于所使用的結(jié)構(gòu)更接近于操作系統(tǒng)執(zhí)行IO的方式:通道和緩沖期。我們可以把它想象成一個(gè)煤礦,通道是一個(gè)包含煤層(數(shù)據(jù))的礦藏,而緩沖器則是派送到礦藏的卡車。卡車滿載煤炭而歸,我們?cè)購(gòu)目ㄜ嚿汐@得煤炭。也就說(shuō),我們并沒有直接好通道交互,我們只和緩沖區(qū)交互。
緩沖區(qū)(Buffer)
緩沖區(qū)本質(zhì)上是一塊可以寫入數(shù)據(jù),然后可以從中讀取數(shù)據(jù)的內(nèi)存。這塊內(nèi)存被包裝成NIO Buffer對(duì)象,并提供了一組方法,用來(lái)方便的訪問該塊內(nèi)存。,在NIO庫(kù)中,所有數(shù)據(jù)都是用緩沖區(qū)處理的。在讀取數(shù)據(jù)時(shí),它是直接讀到緩沖區(qū)中的; 在寫入數(shù)據(jù)時(shí),它也是寫入到緩沖區(qū)中的;任何時(shí)候訪問 NIO 中的數(shù)據(jù),都是將它放到緩沖區(qū)中。而在面向流I/O系統(tǒng)中,所有數(shù)據(jù)都是直接寫入或者直接將數(shù)據(jù)讀取到Stream對(duì)象中。
在NIO中,所有的緩沖區(qū)類型都繼承于抽象類Buffer,最常用的就是ByteBuffer,對(duì)于Java中的基本類型,基本都有一個(gè)具體Buffer類型與之相對(duì)應(yīng),它們之間的繼承關(guān)系如下圖所示:
Buffer基本用法
使用Buffer讀寫數(shù)據(jù)一般遵循以下四個(gè)步驟:
當(dāng)向buffer寫入數(shù)據(jù)時(shí),buffer會(huì)記錄下寫了多少數(shù)據(jù)。一旦要讀取數(shù)據(jù),需要通過flip()方法將Buffer從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到buffer的所有數(shù)據(jù)。
一旦讀完了所有的數(shù)據(jù),就需要清空緩沖區(qū),讓它可以再次被寫入。有兩種方式能清空緩沖區(qū):調(diào)用clear()或compact()方法。clear()方法會(huì)清空整個(gè)緩沖區(qū)。compact()方法只會(huì)清除已經(jīng)讀過的數(shù)據(jù)。任何未讀的數(shù)據(jù)都被移到緩沖區(qū)的起始處,新寫入的數(shù)據(jù)將放到緩沖區(qū)未讀數(shù)據(jù)的后面。
通道(Channel)
通道是一個(gè)對(duì)象,通過它可以讀取和寫入數(shù)據(jù),當(dāng)然了所有數(shù)據(jù)都通過Buffer對(duì)象來(lái)處理。我們永遠(yuǎn)不會(huì)將字節(jié)直接寫入通道中,相反是將數(shù)據(jù)寫入包含一個(gè)或者多個(gè)字節(jié)的緩沖區(qū)。同樣不會(huì)直接從通道中讀取字節(jié),而是將數(shù)據(jù)從通道讀入緩沖區(qū),再?gòu)木彌_區(qū)獲取這個(gè)字節(jié)。
通道類似流,但又有些不同:
- 既可以從通道中讀取數(shù)據(jù),又可以寫數(shù)據(jù)到通道。但流的讀寫通常是單向的。
- 通道可以異步讀寫
- 通道中數(shù)據(jù)總要先讀到Buffer,或者從一個(gè)Buffer寫入
這個(gè)圖看著好混亂,那我們講幾個(gè)比較重要的通道:
- FileChannel : 從文件中讀寫數(shù)據(jù)。
- DatagramChannel :通過UDP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)。
- SocketChannel :通過TCP讀寫網(wǎng)絡(luò)中的數(shù)據(jù)。
- ServerSocketChannel: 監(jiān)聽新進(jìn)來(lái)的TCP連接,像Web服務(wù)器那樣。對(duì)每一個(gè)新進(jìn)來(lái)的連接都會(huì)創(chuàng)建一個(gè)SocketChannel。
Channel基本用法
為了說(shuō)明,下面舉個(gè)讀文件的例子。
public static void main(String[] args) {try {FileInputStream fin = new FileInputStream("test.txt");// get channelFileChannel fc = fin.getChannel();// bufferByteBuffer buffer = ByteBuffer.allocate(1024);//read data to bufferfc.read(buffer);buffer.flip();while(buffer.remaining()>0){byte b = buffer.get();System.out.print((char)b);}fin.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}阻塞IO(常規(guī)IO) VS 非阻塞IO(NIO)
- Java IO的各種流是阻塞的。這意味著,當(dāng)一個(gè)線程調(diào)用read() 或 write()時(shí),該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。
- Java NIO的非阻塞模式,使一個(gè)線程從某通道發(fā)送請(qǐng)求讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取。而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情;非阻塞寫也是如此。一個(gè)線程請(qǐng)求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個(gè)線程同時(shí)可以去做別的事情。 線程通常將非阻塞IO的空閑時(shí)間用于在其它通道上執(zhí)行IO操作,所以一個(gè)單獨(dú)的線程現(xiàn)在可以管理多個(gè)輸入和輸出通道(channel)。
所以這就引入一個(gè)概念,選擇器
選擇器(Selectors)
Selector允許單線程處理多個(gè) Channel。如果你的應(yīng)用打開了多個(gè)連接(通道),但每個(gè)連接的流量都很低,使用Selector就會(huì)很方便。
你可以注冊(cè)多個(gè)通道使用一個(gè)選擇器,然后使用一個(gè)單獨(dú)的線程來(lái)“選擇”通道:這些通道里已經(jīng)有可以處理的輸入,或者選擇已準(zhǔn)備寫入的通道。這種選擇機(jī)制,使得一個(gè)單獨(dú)的線程很容易來(lái)管理多個(gè)通道。
使用
要使用Selector,得向Selector注冊(cè)Channel,然后調(diào)用它的select()方法。這個(gè)方法會(huì)一直阻塞到某個(gè)注冊(cè)的通道有事件就緒。一旦這個(gè)方法返回,線程就可以處理這些事件,事件的例子有如新連接進(jìn)來(lái),數(shù)據(jù)接收等。
轉(zhuǎn)載于:https://www.cnblogs.com/tina-smile/p/5245326.html
總結(jié)
以上是生活随笔為你收集整理的java_IO流之 NIO的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。