摘要:通過協(xié)議向網(wǎng)絡(luò)讀寫數(shù)據(jù)通過協(xié)議向網(wǎng)絡(luò)讀寫數(shù)據(jù)以一個服務(wù)器的形式,監(jiān)聽到來的連接,對每個連接建立一個。
Java NIO 教程 NIO是什么?
它是Java1.4之后出現(xiàn)的IO API,與傳統(tǒng)IO和網(wǎng)絡(luò)API不同,具有非阻塞的特點。
在BIO中我們使用字節(jié)流和字符流。NIO中我們使用channel和buffer。數(shù)據(jù)總是從一個channel中讀取到buffer中,或者從buffer中寫入到channel中。
NIO的意思是一個線程可以讓一個channel將數(shù)據(jù)讀取到buffer中,與此同時,這個線程還可以做其他的事情,線程可以等到數(shù)據(jù)全部進(jìn)入buffer之后再處理數(shù)據(jù),從buffer中寫入線程也是一樣的。
selector:選擇器是一個NIO當(dāng)中的概念,指的是一個對象,能監(jiān)視多個channel發(fā)生的事件(如連接建立,數(shù)據(jù)到達(dá)等)。因此,一個單線程可以監(jiān)視多個channel的數(shù)據(jù)。
Java NIO 總覽Java NIO的三個核心基礎(chǔ)組件,
Channels
Buffers
Selectors
其余的諸如Pipe,F(xiàn)ileLcok都是在使用以上三個核心組件時幫助更好使用的工具類。
Channels和Buffers的關(guān)系所有的IO操作在NIO中都是以Channel開始的。一個Channel就像一個流。從Channel中,數(shù)據(jù)可以被讀取到buffer里,也可以從buffer里寫到Channel中。
基本的Channel實現(xiàn)有以下這些:
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
涵蓋了UDP,TCP以及文件的IO操作。
核心的buffer實現(xiàn)有這些
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
涵蓋了所有的基本數(shù)據(jù)類型(4類8種,除了Boolean)。也有其他的buffer如MappedByteBuffer,此處不講。
selectorsselector允許一個線程來監(jiān)視多個Channel,這在當(dāng)你的應(yīng)用建立了多個連接,但是每個連接吞吐量都較小的時候是可行的。例如:一個聊天服務(wù)器。圖為一個線程使用selector處理三個channel。
要使用一個Selector,你要先注冊這個selector的Channels。然后你調(diào)用selector的select()方法。這個方法會阻塞,直到它注冊的channels當(dāng)中有一個準(zhǔn)備好了的事件發(fā)生了。當(dāng)select()方法返回的時候,線程可以處理這些事件,如新的連接的到來,數(shù)據(jù)收到了等。
NIO ChannelsNIO channel和流很近似但是也有一些不同。
你既可以讀取也可以寫入到channel,流只能讀取或者寫入,inputStream和outputStream。
channel可以異步地讀和寫。
channel永遠(yuǎn)都是從一個buffer中讀或者寫入到一個buffer中去。
channel的實現(xiàn)以下是NIO中最重要的幾個channel的實現(xiàn)。
FileChannel 向文件當(dāng)中讀寫數(shù)據(jù)。
DatagramChannel 通過UDP協(xié)議向網(wǎng)絡(luò)讀寫數(shù)據(jù)
SocketChannel 通過TCP協(xié)議向網(wǎng)絡(luò)讀寫數(shù)據(jù)
ServerSocketChannel 以一個web服務(wù)器的形式,監(jiān)聽到來的TCP連接,對每個連接建立一個SocketChannel。
一個簡單的channel例子使用一個FileChannel將數(shù)據(jù)讀入一個buffer
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); while (bytesRead != -1) { System.out.println("Read " + bytesRead); buf.flip(); while(buf.hasRemaining()){ System.out.print((char) buf.get()); } buf.clear(); bytesRead = inChannel.read(buf); } aFile.close();
buf.flip()的意思是讀寫轉(zhuǎn)換,首先你讀入一個buffer,然后你flip,轉(zhuǎn)換讀寫,然后再從buffer中讀出,buffer的操作接下來會講。
NIO bufferNIO buffer在與NIO Channel交互時使用,數(shù)據(jù)從channel中讀取出來放入buffer,或者從buffer中讀取出來寫入channel。
buffer就是一塊內(nèi)存,你可以寫入數(shù)據(jù),并且在之后讀取它。這塊內(nèi)存被包裝成NIO buffer對象,它提供了一些方法來讓你更簡單地操作內(nèi)存。
buffer的基本使用使用buffer讀寫數(shù)據(jù)基本上分為以下4部操作:
將數(shù)據(jù)寫入buffer
調(diào)用buffer.flip()
將數(shù)據(jù)從buffer中讀取出來
調(diào)用buffer.clear()或者buffer.compact()
在寫buffer的時候,buffer會跟蹤寫入了多少數(shù)據(jù),需要讀buffer的時候,需要調(diào)用flip()來將buffer從寫模式切換成讀模式,讀模式中只能讀取寫入的數(shù)據(jù),而非整個buffer。
當(dāng)數(shù)據(jù)都讀完了,你需要清空buffer以供下次使用,可以有2種方法來操作:
調(diào)用clear()
調(diào)用compact()
區(qū)別:clear方法清空整個buffer,compact方法只清除你已經(jīng)讀取的數(shù)據(jù),未讀取的數(shù)據(jù)會被移到buffer的開頭,此時寫入數(shù)據(jù)會從當(dāng)前數(shù)據(jù)的末尾開始。
一個簡單的buffer使用例子:
RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); //創(chuàng)建一個容量為48的ByteBuffer ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); //從channel中讀(取數(shù)據(jù)然后寫)入buffer //下面是讀取buffer while (bytesRead != -1) { buf.flip(); //轉(zhuǎn)換buffer為讀模式 while(buf.hasRemaining()){ System.out.print((char) buf.get()); // 一次讀取一個byte } buf.clear(); //清空buffer準(zhǔn)備下一次寫入 bytesRead = inChannel.read(buf); } aFile.close();buffer的Capacity,Position和Limit
buffer有3個屬性需要熟悉以理解buffer的工作原理:
容量(Capacity):緩沖區(qū)能夠容納的數(shù)據(jù)元素的最大數(shù)量。容量在緩沖區(qū)創(chuàng)建時被設(shè)定,并且永遠(yuǎn)不能被改變。
上界(Limit):寫模式中等價于buffer的大小,即capacity;讀模式中為當(dāng)前緩沖區(qū)中一共有多少數(shù)據(jù),即可讀的最大位置。這意味著當(dāng)調(diào)用filp()方法切換成讀模式時,limit的值變成position的值,而position重新指向0.
位置(Position):下一個要被讀或?qū)懙脑氐奈恢?。初始化?,buffer滿時,position最大值為capacity-1。切換成讀模式的時候,position指向0。Position會自動由相應(yīng)的 get( )和 put( )函數(shù)更新。
position和limit的值在讀/寫模式中是不一樣的。
capacity的值永遠(yuǎn)表示buffer的大小。
下圖解釋了在讀/寫模式中Capacity,Position和Limit的意思。
buffer的種類Java NIO中有以下這些buffer種類:
ByteBuffer
MappedByteBuffer //比較特殊,會在以后講解
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
創(chuàng)建一個buffer獲得一個buffer 之前必須先分配一塊內(nèi)存,每個buffer類都有一個靜態(tài)方法allocate() 來做這件事。
下例為創(chuàng)建一個容量為48byte的ByteBuffer:
ByteBuffer buf = ByteBuffer.allocate(48);
創(chuàng)建一個1024個字符的CharBuffer
CharBuffer buf = CharBuffer.allocate(1024);
寫入buffer的方法有2種:
1.從一個channel中寫入buffer。
2.調(diào)用buffer的put()方法來自行寫入數(shù)據(jù)。
例:
int bytesRead = inChannel.read(buf); //從channel讀入buffer
buf.put(127); //自行寫入buffer
put方法有很多的重載形式。以供你用各種不同的方法寫入buffer中,比如從一個特定的position,或者寫入一個array,詳見JavaDoc。
flip()flip方法將寫模式切換成讀模式,調(diào)用flip()方法會將limit設(shè)置為position,將position設(shè)置回0。
換句話說,position標(biāo)志著寫模式中寫到哪里,切換成讀模式之后,limit標(biāo)志著之前寫到哪里,也就是現(xiàn)在能讀到哪里。
從buffer中讀取數(shù)據(jù)有2種方法可以從buffer中讀取數(shù)據(jù)。
1.從buffer中讀取數(shù)據(jù)到channel中。
2.使用buffer的get()方法自行從buffer中讀出數(shù)據(jù)。
例子:
//從buffer中讀取數(shù)據(jù)到channel中 int bytesWritten = inChannel.write(buf); //使用buffer的get()方法自行從buffer中讀出數(shù)據(jù) byte aByte = buf.get();
get方法有很多的重載形式。以供你用各種不同的方法讀取buffer中的數(shù)據(jù)。例如從特定位置讀取數(shù)據(jù),或者讀一個數(shù)組出來。詳見JavaDoc。
rewind()rewind()方法將position設(shè)置為0,但是不會動buffer里的數(shù)據(jù),這樣可以從頭開始重新讀取數(shù)據(jù),limit的值不會變,這意味著limit依舊標(biāo)志著能讀多少數(shù)據(jù)。
clear()和compact()當(dāng)你讀完所有的數(shù)據(jù)想要重新寫入數(shù)據(jù)時,你可以調(diào)用clear或者compact方法。
當(dāng)你調(diào)用clear()方法的時候,position被設(shè)置為0,limit被設(shè)置為capacity,換句話說,buffer的數(shù)據(jù)雖然都還在,但是buffer被初始化了,處于可以被重寫的狀態(tài)。
這也就意味著如果buffer中還有沒被讀取的數(shù)據(jù),在執(zhí)行clear之后,你無法知道數(shù)據(jù)讀到哪兒了,剩下的數(shù)據(jù)還有多少。
如果還有沒有讀完的數(shù)據(jù),但是你想先寫數(shù)據(jù),可以用compact()方法,這樣未讀數(shù)據(jù)會放在buffer前端,可以在未讀數(shù)據(jù)之后跟著寫新的數(shù)據(jù)。compact()會復(fù)制未讀數(shù)據(jù)到buffer前端,然后設(shè)置position為未讀數(shù)據(jù)單位后面緊跟的位置。limit還是設(shè)置為capacity,這和clear是一樣的?,F(xiàn)在buffer處于可以寫的狀態(tài),但是不會覆蓋之前未讀完的數(shù)據(jù)。
mark()和reset()你可以通過調(diào)用buffer.mark()來mark一個buffer中給定的位置。然后你就可以用buffer.reset()方法來講position設(shè)置回之前mark的位置。
例子:
buffer.mark(); //調(diào)用buffer.get()方法若干次,e.g. 比如在做parsing的時候 buffer.reset(); //set position back to mark.equals() 和 compareTo()
使用這2種方法能夠比較2個buffer。
equals()
equals()方法用于判斷2個buffer是否相等,2個buffer是equal的,當(dāng)它們:
是同一種數(shù)據(jù)類型的buffer。
buffer中未讀取的bytes,chars等數(shù)據(jù)個數(shù)是一樣的,即(limit-position)相等,capacity不需要相等,剩余數(shù)據(jù)的索引也不需要相等。
未讀取的bytes,chars等內(nèi)容是一模一樣的,即各自[position,limit-1]索引的數(shù)據(jù)要完全相等。
如你所見,equals()方法只比較buffer的部分內(nèi)容,而不是buffer中所有的數(shù)據(jù),事實上,它只比較buffer中剩余的元素是否一樣。
compareTo()
compareTo()方法比較兩個buffer的剩余元素(字節(jié),字符等),用于例如: 排序。
在下列情況下,緩沖區(qū)被認(rèn)為比另一個緩沖區(qū)“小”:
比較是針對每個緩沖區(qū)你剩余數(shù)據(jù)(從 position 到 limit)進(jìn)行的,與它們在 equals() 中的方式相同,直到不相等的元素被發(fā)現(xiàn)或者到達(dá)緩沖區(qū)的上界。如果一個緩沖區(qū)在不相等元素發(fā)現(xiàn)前已經(jīng)被耗盡,較短的緩沖區(qū)被認(rèn)為是小于較長的緩沖區(qū)。
if (buffer1.compareTo(buffer2) < 0) { // do sth, it means buffer2 < buffer1,not buffer1 < buffer2 doSth(); }
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/76360.html
摘要:而我們現(xiàn)在都已經(jīng)發(fā)布了,的都不知道,這有點說不過去了。而對一個的讀寫也會有響應(yīng)的描述符,稱為文件描述符,描述符就是一個數(shù)字,指向內(nèi)核中的一個結(jié)構(gòu)體文件路徑,數(shù)據(jù)區(qū)等一些屬性。 前言 只有光頭才能變強 回顧前面: 給女朋友講解什么是代理模式 包裝模式就是這么簡單啦 本來我預(yù)想是先來回顧一下傳統(tǒng)的IO模式的,將傳統(tǒng)的IO模式的相關(guān)類理清楚(因為IO的類很多)。 但是,發(fā)現(xiàn)在整理的過程已...
摘要:操作系統(tǒng)是能夠獲取到事件操作完成的事件,基于回調(diào)函數(shù)機制和操作系統(tǒng)的操作控制實現(xiàn)事件檢測機制。 前面的文章NIO基礎(chǔ)知識介紹了Java NIO的一些基本的類及功能說明,Java NIO是用來替換java 傳統(tǒng)IO的,NIO的一些新的特性在網(wǎng)絡(luò)交互方面會更加的明顯。 Java 傳統(tǒng)IO的弊端 ????基于JVM來實現(xiàn)每個通道的輪詢檢查通道狀態(tài)的方法是可行的,但仍然是有問題的,檢查每個通道...
摘要:后改良為用線程池的方式代替新增線程,被稱為偽異步。最大的問題是阻塞,同步。每次請求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會被注冊在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒有采用的多路復(fù)用器,而是使用異步通道的概念。 Netty是一個提供異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架,用以快速開發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶端程序。Netty簡化了網(wǎng)絡(luò)程序的開發(fā),是很多框架和公司...
摘要:后改良為用線程池的方式代替新增線程,被稱為偽異步。最大的問題是阻塞,同步。每次請求都由程序執(zhí)行并返回,這是同步的缺陷。這些都會被注冊在多路復(fù)用器上。多路復(fù)用器提供選擇已經(jīng)就緒狀態(tài)任務(wù)的能力。并沒有采用的多路復(fù)用器,而是使用異步通道的概念。 Netty是一個提供異步事件驅(qū)動的網(wǎng)絡(luò)應(yīng)用框架,用以快速開發(fā)高性能、高可靠的網(wǎng)絡(luò)服務(wù)器和客戶端程序。Netty簡化了網(wǎng)絡(luò)程序的開發(fā),是很多框架和公司...
閱讀 2871·2023-04-25 22:15
閱讀 1886·2021-11-19 09:40
閱讀 2233·2021-09-30 09:48
閱讀 3317·2021-09-03 10:36
閱讀 2119·2021-08-30 09:48
閱讀 1952·2021-08-24 10:00
閱讀 2792·2019-08-30 15:54
閱讀 770·2019-08-30 15:54