NIO笔记
JAVA NIO,是區(qū)別于JAVA IO的NEW IO。和普通的IO存在一定的區(qū)別,先記下這么幾個(gè)字吧:
走通道,用緩存,選擇器,非阻塞
走通道
NIO有三大組成:Channel(通道)、Buffer(緩存)、Selector(選擇器)
其中通道是指的實(shí)現(xiàn)java.nio.channels.Channel接口的對(duì)象,常見的對(duì)象有以下幾種:
FileChannel(文件通道)
DatagramChannel(收發(fā)UDP包的通道)
SocketChannel(連接到TCP網(wǎng)絡(luò)套接字的通道)
ServerSocketChannel(可以監(jiān)聽新進(jìn)來的TCP連接的通道)
相比較于IO的時(shí)候,輸入輸出需要用InputStream,OutputStream兩種,NIO的的通道只有一種,以FileChannel為例。
打開:
讀取:
ByteBuffer buf = ByteBuffer.allocate(48); // nio的buffer int bytesRead = inChannel.read(buf); // 初始化寫入到buffer while (bytesRead != -1) {buf.flip(); // 切換讀寫模式while(buf.hasRemaining()){System.out.print((char) buf.get()); // 讀取buff的內(nèi)容}buf.clear(); // buffer清空,使之可以再次寫入bytesRead = inChannel.read(buf); // 寫入到buffer }寫入:
inChannel.write(buf);可以看到,使用FileChannel不管是讀取還是寫入,都是使用了ByteBuffer作為中轉(zhuǎn),這個(gè)ByteBuffer,就是NIO中的緩存了。
用緩存
緩存類型:
緩存是實(shí)現(xiàn)了java.nio.Buffer抽象類接口的對(duì)象。
對(duì)于部分IO來說,其對(duì)象內(nèi)部也存在緩存,但都是對(duì)象的成員變量,而NIO中的,緩存需要單獨(dú)使用,走【數(shù)據(jù)】->【緩存】->【通道】->【對(duì)象】,或者【對(duì)象】->【通道】->【緩存】->【數(shù)據(jù)】的方式,進(jìn)行數(shù)據(jù)的流通。
緩存有很多種,除了boolean以外,基本類型全部包括。個(gè)人感覺有的類似于ObjectInputStream中writeByte、writeInt等等的方法,把這些方法抽象為一個(gè)對(duì)象的感覺。
ByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer
例子:
首先,可以看到,java.nio.Buffer的源碼中的以下記得成員,這是buffer的關(guān)鍵:
private int position = 0; private int limit; private int capacity;其中:
capacity:內(nèi)存塊,初始化時(shí)設(shè)置,限制緩存空間大小。
position:當(dāng)前位置。寫模式時(shí)初始為零,寫一次移動(dòng)一下。切換到讀模式時(shí),重置為0。position最大可為capacity – 1。
limit:寫模式時(shí),同capacity,一次寫入大小。切換到讀模式時(shí), limit表示你最多能讀到多少數(shù)據(jù),此時(shí),limit會(huì)被設(shè)置成寫模式下的position值。
聚集和分散:
簡單的說,buffer使用的時(shí)候,可以一個(gè)數(shù)組一起使用,這種使用方式類似水流,先填滿一個(gè),再填滿另一個(gè)。
Scattering Reads(聚集)是指數(shù)據(jù)從一個(gè)channel讀取到多個(gè)buffer中。
read()方法按照buffer在數(shù)組中的順序?qū)腸hannel中讀取的數(shù)據(jù)寫入到buffer,當(dāng)一個(gè)buffer被寫滿后,channel緊接著向另一個(gè)buffer中寫。
Gathering Writes(分散)是指數(shù)據(jù)從多個(gè)buffer寫入到同一個(gè)channel。
channel.write(bufferArray);使用緩存:
初始化:使用allocate方法,設(shè)定capacity以初始化
ByteBuffer byteBuf = ByteBuffer.allocate(48);寫入緩存:直降使用緩存的put方法,或者使用通道的read方法進(jìn)行寫入
inChannel.read(buf); // 通道寫入到bufferbuf.put(100); // put寫入模式切換:寫入后,進(jìn)行讀取時(shí),需要調(diào)用flip()方法,進(jìn)行模式切換(實(shí)際是改變position位置)
buf.flip(); // 切換讀寫模式重置:讀取后想重新讀取,使用rewind()方法。
buf.rewind(); // buffer重置,使之可以再次讀取清空:想要再次寫入,或者清空寫入內(nèi)容時(shí),使用clear()方法
buf.clear(); // buffer清空,使之可以再次寫入
選擇器
選擇器即Selector,簡單的說,就是用于管理多個(gè)Channel的對(duì)象,感覺有的類似于線程池,但Selector確實(shí)是單線程對(duì)象,通過注冊(cè)->接收->處理的過程,對(duì)多個(gè)Channel進(jìn)行管理。
其中將【選擇器】同【通道】關(guān)聯(lián)起來的線,即為選擇鍵,SelectionKey。
總之先記著,選擇器使用靜態(tài)實(shí)例創(chuàng)建,使用注冊(cè)通道返回選擇鍵的方式進(jìn)行關(guān)聯(lián)。
SelectionKey.OP_ACCEPT標(biāo)識(shí),是Selector要監(jiān)視通道的那些動(dòng)作。當(dāng)通道的動(dòng)作處在就緒狀態(tài)時(shí),Selector監(jiān)視到,然后可進(jìn)行操作。
關(guān)于SelectionKey,既然是Channel和Selector的管理,那么進(jìn)行操作的時(shí)候,也是對(duì)SelectionKey進(jìn)行操作了。可以使用isAcceptable(),isConnectable(),isReadable(),isWritable(),來判斷Channel的是否可以進(jìn)行對(duì)應(yīng)的操作。isValid判斷是否有數(shù)據(jù)(緩存)。attach來進(jìn)行綁定和解綁等等。
因此,一般Selector的使用方式是弄個(gè)循環(huán),使用selector.selectedKeys()的方法渠道所有就緒,能操作的SelectionKey,如下:
非阻塞
由于是同步非阻塞的,所以其原理基本上就是在設(shè)置好之后,利用循環(huán)不斷遍歷Channel,判斷其狀態(tài),有可操作的地方就處理,沒有則繼續(xù)判斷而已。
其中,由于使用了緩存通道分離的方式,最大的優(yōu)勢在于當(dāng)有可進(jìn)行的操作的時(shí)候,實(shí)際上通道已經(jīng)準(zhǔn)備就緒,可以立即執(zhí)行,省去了讀取的時(shí)間。如下,其他方法時(shí),以下read的時(shí)候,會(huì)阻塞花費(fèi)較多時(shí)間。而NIO的時(shí)候,由于已經(jīng)換成,會(huì)馬上讀取完畢,可執(zhí)行下面的操作。
轉(zhuǎn)載于:https://www.cnblogs.com/changfanchangle/p/9092450.html
總結(jié)
- 上一篇: (五)Cisco dhcp snoopi
- 下一篇: Beta阶段——第4篇 Scrum 冲刺