【学习】009 NIO编程
?
?
NIO概述
什么是NIO?
Java NIO(New IO)是一個(gè)可以替代標(biāo)準(zhǔn)Java IO API的IO API(從Java 1.4開(kāi)始),Java NIO提供了與標(biāo)準(zhǔn)IO不同的IO工作方式。
Java NIO: Channels and Buffers(通道和緩沖區(qū))
標(biāo)準(zhǔn)的IO基于字節(jié)流和字符流進(jìn)行操作的,而NIO是基于通道(Channel)和緩沖區(qū)(Buffer)進(jìn)行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)中,或者從緩沖區(qū)寫(xiě)入到通道中。
Java NIO: Non-blocking IO(非阻塞IO)
Java NIO可以讓你非阻塞的使用IO,例如:當(dāng)線程從通道讀取數(shù)據(jù)到緩沖區(qū)時(shí),線程還是可以進(jìn)行其他事情。當(dāng)數(shù)據(jù)被寫(xiě)入到緩沖區(qū)時(shí),線程可以繼續(xù)處理它。從緩沖區(qū)寫(xiě)入通道也類似。
Java NIO: Selectors(選擇器)
Java NIO引入了選擇器的概念,選擇器用于監(jiān)聽(tīng)多個(gè)通道的事件(比如:連接打開(kāi),數(shù)據(jù)到達(dá))。因此,單個(gè)的線程可以監(jiān)聽(tīng)多個(gè)數(shù)據(jù)通道。
注意:傳統(tǒng)IT是單向。 NIO類似
區(qū)別
| IO | NIO |
| 面向流 | 面向緩沖區(qū) |
| 阻塞IO | 非阻塞IO |
| 無(wú) | 選擇器 |
?
Buffer的數(shù)據(jù)存取
一個(gè)用于特定基本數(shù)據(jù)類行的容器。有java.nio包定義的,所有緩沖區(qū)都是抽象類Buffer的子類。
Java NIO中的Buffer主要用于與NIO通道進(jìn)行交互,數(shù)據(jù)是從通道讀入到緩沖區(qū),從緩沖區(qū)寫(xiě)入通道中的。
Buffer就像一個(gè)數(shù)組,可以保存多個(gè)相同類型的數(shù)據(jù)。根據(jù)類型不同(boolean除外),有以下Buffer常用子類:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
Buffer的概述
1)容量(capacity):表示Buffer最大數(shù)據(jù)容量,緩沖區(qū)容量不能為負(fù),并且建立后不能修改。
2)限制(limit):第一個(gè)不應(yīng)該讀取或者寫(xiě)入的數(shù)據(jù)的索引,即位于limit后的數(shù)據(jù)不可以讀寫(xiě)。緩沖區(qū)的限制不能為負(fù),并且不能大于其容量(capacity)。
3)位置(position):下一個(gè)要讀取或?qū)懭氲臄?shù)據(jù)的索引。緩沖區(qū)的位置不能為負(fù),并且不能大于其限制(limit)。
4)標(biāo)記(mark)與重置(reset):標(biāo)記是一個(gè)索引,通過(guò)Buffer中的mark()方法指定Buffer中一個(gè)特定的position,之后可以通過(guò)調(diào)用reset()方法恢復(fù)到這個(gè)position。
?
package com.hongmoshui.sum;import java.nio.ByteBuffer;/*** (緩沖區(qū))buffer 用于NIO存儲(chǔ)數(shù)據(jù) 支持多種不同的數(shù)據(jù)類型 <br>* 1.byteBuffer <br>* 2.charBuffer <br>* 3.shortBuffer<br>* 4.IntBuffer<br>* 5.LongBuffer<br> * 6.FloatBuffer <br>* 7.DubooBuffer <br>* 上述緩沖區(qū)管理的方式 幾乎<br>* 通過(guò)allocate() 獲取緩沖區(qū) <br>* 二、緩沖區(qū)核心的方法 put 存入數(shù)據(jù)到緩沖區(qū) get <br> 獲取緩沖區(qū)數(shù)據(jù) flip 開(kāi)啟讀模式* 三、緩沖區(qū)四個(gè)核心屬性<br>* capacity:緩沖區(qū)最大容量,一旦聲明不能改變。 limit:界面(緩沖區(qū)可以操作的數(shù)據(jù)大小) limit后面的數(shù)據(jù)不能讀寫(xiě)。* position:緩沖區(qū)正在操作的位置*/ public class Test004 {public static void main(String[] args) {// 1.指定緩沖區(qū)大小1024ByteBuffer buf = ByteBuffer.allocate(1024);System.out.println("--------------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());// 2.向緩沖區(qū)存放5個(gè)數(shù)據(jù)buf.put("abcd1".getBytes());System.out.println("--------------------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());// 3.開(kāi)啟讀模式 buf.flip();System.out.println("----------開(kāi)啟讀模式...----------");System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());byte[] bytes = new byte[buf.limit()];buf.get(bytes);System.out.println(new String(bytes, 0, bytes.length));System.out.println("----------重復(fù)讀模式...----------");// 4.開(kāi)啟重復(fù)讀模式 buf.rewind();System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());byte[] bytes2 = new byte[buf.limit()];buf.get(bytes2);System.out.println(new String(bytes2, 0, bytes2.length));// 5.clean 清空緩沖區(qū) 數(shù)據(jù)依然存在,只不過(guò)數(shù)據(jù)被遺忘System.out.println("----------清空緩沖區(qū)...----------");buf.clear();System.out.println(buf.position());System.out.println(buf.limit());System.out.println(buf.capacity());System.out.println((char)buf.get());}}make與rest用法
標(biāo)記(mark)與重置(reset):標(biāo)記是一個(gè)索引,通過(guò)Buffer中的mark()方法指定Buffer中一個(gè)特定的position,之后可以通過(guò)調(diào)用reset()方法恢復(fù)到這個(gè)position。
package com.hongmoshui.sum;import java.nio.ByteBuffer;public class Test002 {public static void main(String[] args) {ByteBuffer buf = ByteBuffer.allocate(1024);String str = "abcd1";buf.put(str.getBytes());// 開(kāi)啟讀取模式 buf.flip();byte[] dst = new byte[buf.limit()];buf.get(dst, 0, 2);buf.mark();System.out.println(new String(dst, 0, 2));System.out.println(buf.position());buf.get(dst, 2, 2);System.out.println(new String(dst, 2, 2));System.out.println(buf.position());buf.reset();System.out.println("重置恢復(fù)到mark位置..");System.out.println(buf.position());}}直接緩沖區(qū)與非直接緩沖區(qū)別
非直接緩沖區(qū):通過(guò) allocate() 方法分配緩沖區(qū),將緩沖區(qū)建立在 JVM 的內(nèi)存中
直接緩沖區(qū):通過(guò) allocateDirect() 方法分配直接緩沖區(qū),將緩沖區(qū)建立在物理內(nèi)存中。可以提高效率
字節(jié)緩沖區(qū)要么是直接的,要么是非直接的。如果為直接字節(jié)緩沖區(qū),則 Java 虛擬機(jī)會(huì)盡最大努力直接在此緩沖區(qū)上執(zhí)行本機(jī) I/O 操作。也就是說(shuō),在每次調(diào)用基礎(chǔ)操作系統(tǒng)的一個(gè)本機(jī) I/O 操作之前(或之后),虛擬機(jī)都會(huì)盡量避免將緩沖區(qū)的內(nèi)容復(fù)制到中間緩沖區(qū)中(或從中間緩沖區(qū)中復(fù)制內(nèi)容)。
直接字節(jié)緩沖區(qū)可以通過(guò)調(diào)用此類的 allocateDirect() 工廠方法來(lái)創(chuàng)建。此方法返回的緩沖區(qū)進(jìn)行分配和取消分配所需成本通常高于非直接緩沖區(qū)。直接緩沖區(qū)的內(nèi)容可以駐留在常規(guī)的垃圾回收堆之外,因此,它們對(duì)應(yīng)用程序的內(nèi)存需求量造成的影響可能并不明顯。所以,建議將直接緩沖區(qū)主要分配給那些易受基礎(chǔ)系統(tǒng)的本機(jī) I/O 操作影響的大型、持久的緩沖區(qū)。一般情況下,最好僅在直接緩沖區(qū)能在程序性能方面帶來(lái)明顯好處時(shí)分配它們。
直接字節(jié)緩沖區(qū)還可以通過(guò) FileChannel 的 map() 方法 將文件區(qū)域直接映射到內(nèi)存中來(lái)創(chuàng)建。該方法返回MappedByteBuffer 。 Java 平臺(tái)的實(shí)現(xiàn)有助于通過(guò) JNI 從本機(jī)代碼創(chuàng)建直接字節(jié)緩沖區(qū)。如果以上這些緩沖區(qū)中的某個(gè)緩沖區(qū)實(shí)例指的是不可訪問(wèn)的內(nèi)存區(qū)域,則試圖訪問(wèn)該區(qū)域不會(huì)更改該緩沖區(qū)的內(nèi)容,并且將會(huì)在訪問(wèn)期間或稍后的某個(gè)時(shí)間導(dǎo)致拋出不確定的異常。
字節(jié)緩沖區(qū)是直接緩沖區(qū)還是非直接緩沖區(qū)可通過(guò)調(diào)用其 isDirect() 方法來(lái)確定。提供此方法是為了能夠在性能關(guān)鍵型代碼中執(zhí)行顯式緩沖區(qū)管理。
package com.hongmoshui.sum;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption;public class Test005 {public static void main(String[] args) throws IOException{test1();test2();}// 使用直接緩沖區(qū)完成文件的復(fù)制(內(nèi)存映射文件)public static void test2() throws IOException{long start = System.currentTimeMillis();FileChannel inChannel = FileChannel.open(Paths.get("D:/test.txt"), StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("D:/test2.txt"), StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);// 內(nèi)存映射文件MappedByteBuffer inMappedByteBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMappedByteBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());// 直接對(duì)緩沖區(qū)進(jìn)行數(shù)據(jù)的讀寫(xiě)操作byte[] dsf = new byte[inMappedByteBuf.limit()];inMappedByteBuf.get(dsf);outMappedByteBuffer.put(dsf);inChannel.close();outChannel.close();long end = System.currentTimeMillis();System.out.println(end - start);}// 1.利用通道完成文件的復(fù)制(非直接緩沖區(qū))public static void test1() throws IOException{ // 4400long start = System.currentTimeMillis();FileInputStream fis = new FileInputStream("D:/test.txt");FileOutputStream fos = new FileOutputStream("D:/test2.txt");// ①獲取通道FileChannel inChannel = fis.getChannel();FileChannel outChannel = fos.getChannel();// ②分配指定大小的緩沖區(qū)ByteBuffer buf = ByteBuffer.allocate(1024);while (inChannel.read(buf) != -1){buf.flip();// 切換為讀取數(shù)據(jù)// ③將緩沖區(qū)中的數(shù)據(jù)寫(xiě)入通道中 outChannel.write(buf);buf.clear();}outChannel.close();inChannel.close();fos.close();fis.close();long end = System.currentTimeMillis();System.out.println(end - start);}}通道(Channel)的原理獲取
通道表示打開(kāi)到 IO 設(shè)備(例如:文件、套接字)的連接。若需要使用 NIO 系統(tǒng),需要獲取用于連接 IO 設(shè)備的通道以及用于容納數(shù)據(jù)的緩沖區(qū)。然后操作緩沖區(qū),對(duì)數(shù)據(jù)進(jìn)行處理。Channel 負(fù)責(zé)傳輸, Buffer 負(fù)責(zé)存儲(chǔ)。通道是由 java.nio.channels 包定義的。 Channel 表示 IO 源與目標(biāo)打開(kāi)的連接。Channel 類似于傳統(tǒng)的“流”。只不過(guò) Channel本身不能直接訪問(wèn)數(shù)據(jù), Channel 只能與Buffer 進(jìn)行交互。
java.nio.channels.Channel 接口:
? ???????? |--FileChannel
? ???????? |--SocketChannel
? ???????? |--ServerSocketChannel
? ???????? |--DatagramChannel
? 獲取通道
? 1. Java 針對(duì)支持通道的類提供了 getChannel() 方法
? ???????? 本地 IO:
? ???????? FileInputStream/FileOutputStream
? ???????? RandomAccessFile
? ???????? 網(wǎng)絡(luò)IO:
? ???????? Socket
? ???????? ServerSocket
? ??? ????? DatagramSocket??
? 2. 在 JDK 1.7 中的 NIO.2 針對(duì)各個(gè)通道提供了靜態(tài)方法 open()
? 3. 在 JDK 1.7 中的 NIO.2 的 Files 工具類的 newByteChannel()
package com.hongmoshui.sum;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption;import org.junit.Test;public class Test006 {@Test// 使用直接緩沖區(qū)完成文件的復(fù)制(內(nèi)存映射文件)public void test2() throws IOException{FileChannel inChannel = FileChannel.open(Paths.get("1.png"), StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("2.png"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);// 映射文件MappedByteBuffer inMapperBuff = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMapperBuff = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());// 直接對(duì)緩沖區(qū)進(jìn)行數(shù)據(jù)讀寫(xiě)操作byte[] dst = new byte[inMapperBuff.limit()];inMapperBuff.get(dst);outMapperBuff.put(dst);outChannel.close();inChannel.close();}@Test// 1.利用通道完成文件復(fù)制(非直接緩沖區(qū))public void test1() throws IOException{FileInputStream fis = new FileInputStream("1.png");FileOutputStream fos = new FileOutputStream("2.png");// ①獲取到通道FileChannel inChannel = fis.getChannel();FileChannel outChannel = fos.getChannel();// ②分配指定大小的緩沖區(qū)ByteBuffer buf = ByteBuffer.allocate(1024);while (inChannel.read(buf) != -1){buf.flip();// 切換到讀取模式 outChannel.write(buf);buf.clear();// 清空緩沖區(qū) }// 關(guān)閉連接 outChannel.close();inChannel.close();fos.close();fis.close();}}直接緩沖區(qū)與非直接緩沖耗時(shí)計(jì)算
package com.hongmoshui.sum;import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.Paths; import java.nio.file.StandardOpenOption;import org.junit.Test;public class Test007 {@Test// 使用直接緩沖區(qū)完成文件的復(fù)制(內(nèi)存映射文件) //428、357public void test2() throws IOException{long startTime = System.currentTimeMillis();FileChannel inChannel = FileChannel.open(Paths.get("f://1.mp4"), StandardOpenOption.READ);FileChannel outChannel = FileChannel.open(Paths.get("f://2.mp4"), StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);// 映射文件MappedByteBuffer inMapperBuff = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());MappedByteBuffer outMapperBuff = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size());// 直接對(duì)緩沖區(qū)進(jìn)行數(shù)據(jù)讀寫(xiě)操作byte[] dst = new byte[inMapperBuff.limit()];inMapperBuff.get(dst);outMapperBuff.put(dst);outChannel.close();inChannel.close();long endTime = System.currentTimeMillis();System.out.println("內(nèi)存映射文件耗時(shí):" + (endTime - startTime));}@Test// 1.利用通道完成文件復(fù)制(非直接緩沖區(qū))public void test1() throws IOException{ // 11953 、3207、3337long startTime = System.currentTimeMillis();FileInputStream fis = new FileInputStream("f://1.mp4");FileOutputStream fos = new FileOutputStream("f://2.mp4");// ①獲取到通道FileChannel inChannel = fis.getChannel();FileChannel outChannel = fos.getChannel();// ②分配指定大小的緩沖區(qū)ByteBuffer buf = ByteBuffer.allocate(1024);while (inChannel.read(buf) != -1){buf.flip();// 切換到讀取模式 outChannel.write(buf);buf.clear();// 清空緩沖區(qū) }// 關(guān)閉連接 outChannel.close();inChannel.close();fos.close();fis.close();long endTime = System.currentTimeMillis();System.out.println("非緩沖區(qū):" + (endTime - startTime));}}分散讀取與聚集寫(xiě)入
分散讀取(scattering Reads):將通道中的數(shù)據(jù)分散到多個(gè)緩沖區(qū)中
聚集寫(xiě)入(gathering Writes):將多個(gè)緩沖區(qū)的數(shù)據(jù)聚集到通道中
?
package com.hongmoshui.sum;import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public class Test008 {public static void main(String[] args) throws IOException{RandomAccessFile raf1 = new RandomAccessFile("test.txt", "rw");// 1.獲取通道FileChannel channel = raf1.getChannel();// 2.分配指定大小的指定緩沖區(qū)ByteBuffer buf1 = ByteBuffer.allocate(100);ByteBuffer buf2 = ByteBuffer.allocate(1024);// 3.分散讀取ByteBuffer[] bufs ={ buf1, buf2 };channel.read(bufs);for (ByteBuffer byteBuffer : bufs){// 切換為讀取模式 byteBuffer.flip();}System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));System.out.println("------------------分算讀取線分割--------------------");System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));// 聚集寫(xiě)入RandomAccessFile raf2 = new RandomAccessFile("test2.txt", "rw");FileChannel channel2 = raf2.getChannel();channel2.write(bufs);}}字符集 Charset
編碼:字符串->字節(jié)數(shù)組
解碼:字節(jié)數(shù)組 -> 字符串
package com.hongmoshui.sum;import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder;public class Test009 {public static void main(String[] args) throws CharacterCodingException{// 獲取編碼器【utf-8編碼,中文占3個(gè)字節(jié),英文占1個(gè)字節(jié)】Charset cs1 = Charset.forName("UTF-8");// 獲取編碼器CharsetEncoder ce = cs1.newEncoder();// 獲取解碼器CharsetDecoder cd = cs1.newDecoder();CharBuffer cBuf = CharBuffer.allocate(1024);cBuf.put("洪墨水,哈哈哈!");cBuf.flip();// 編碼ByteBuffer bBuf = ce.encode(cBuf);for (int i = 0; i < 24; i++){System.out.println(bBuf.get());}// 解碼 bBuf.flip();CharBuffer cBuf2 = cd.decode(bBuf);System.out.println(cBuf2.toString());System.out.println("-------------------------------------");Charset cs2 = Charset.forName("UTF-8");bBuf.flip();CharBuffer cbeef = cs2.decode(bBuf);System.out.println(cbeef.toString());}}NIO同步阻塞與同步非阻塞
BIO與NIO
IO(BIO)和NIO區(qū)別:其本質(zhì)就是阻塞和非阻塞的區(qū)別
阻塞概念:應(yīng)用程序在獲取網(wǎng)絡(luò)數(shù)據(jù)的時(shí)候,如果網(wǎng)絡(luò)傳輸數(shù)據(jù)很慢,就會(huì)一直等待,直到傳輸完畢為止。
非阻塞概念:應(yīng)用程序直接可以獲取已經(jīng)準(zhǔn)備就緒好的數(shù)據(jù),無(wú)需等待。
IO為同步阻塞形式,NIO為同步非阻塞形式,NIO并沒(méi)有實(shí)現(xiàn)異步,在JDK1.7后升級(jí)NIO庫(kù)包,支持異步非阻塞
同學(xué)模型NIO2.0(AIO)
BIO:同步阻塞式IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開(kāi)銷,當(dāng)然可以通過(guò)線程池機(jī)制改善。?
NIO:同步非阻塞式IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)請(qǐng)求一個(gè)線程,即客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上,多路復(fù)用器輪詢到連接有I/O請(qǐng)求時(shí)才啟動(dòng)一個(gè)線程進(jìn)行處理。?
AIO(NIO.2):異步非阻塞式IO,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由OS先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線程進(jìn)行處理。?
?
同步時(shí),應(yīng)用程序會(huì)直接參與IO讀寫(xiě)操作,并且我們的應(yīng)用程序會(huì)直接阻塞到某一個(gè)方法上,直到數(shù)據(jù)準(zhǔn)備就緒:
或者采用輪訓(xùn)的策略實(shí)時(shí)檢查數(shù)據(jù)的就緒狀態(tài),如果就緒則獲取數(shù)據(jù).
異步時(shí),則所有的IO讀寫(xiě)操作交給操作系統(tǒng),與我們的應(yīng)用程序沒(méi)有直接關(guān)系,我們程序不需要關(guān)系IO讀寫(xiě),當(dāng)操作
系統(tǒng)完成了IO讀寫(xiě)操作時(shí),會(huì)給我們應(yīng)用程序發(fā)送通知,我們的應(yīng)用程序直接拿走數(shù)據(jù)極即可。
偽異步
由于BIO一個(gè)客戶端需要一個(gè)線程去處理,因此我們進(jìn)行優(yōu)化,后端使用線程池來(lái)處理多個(gè)客戶端的請(qǐng)求接入,形成客戶端個(gè)數(shù)M:線程池最大的線程數(shù)N的比例關(guān)系,其中M可以遠(yuǎn)遠(yuǎn)大于N,通過(guò)線程池可以靈活的調(diào)配線程資源,設(shè)置線程的最大值,防止由于海量并發(fā)接入導(dǎo)致線程耗盡。
原理:
當(dāng)有新的客戶端接入時(shí),將客戶端的Socket封裝成一個(gè)Task(該Task任務(wù)實(shí)現(xiàn)了java的Runnable接口)投遞到后端的線程池中進(jìn)行處理,由于線程池可以設(shè)置消息隊(duì)列的大小以及線程池的最大值,因此,它的資源占用是可控的,無(wú)論多少個(gè)客戶端的并發(fā)訪問(wèn),都不會(huì)導(dǎo)致資源的耗盡或宕機(jī)。
IO模型關(guān)系
什么是阻塞
阻塞概念:應(yīng)用程序在獲取網(wǎng)絡(luò)數(shù)據(jù)的時(shí)候,如果網(wǎng)絡(luò)傳輸很慢,那么程序就一直等著,直接到傳輸完畢。
什么是非阻塞
應(yīng)用程序直接可以獲取已經(jīng)準(zhǔn)備好的數(shù)據(jù),無(wú)需等待.
IO為同步阻塞形式,NIO為同步非阻塞形式。NIO沒(méi)有實(shí)現(xiàn)異步,在JDK1.7之后,升級(jí)了NIO庫(kù)包
,支持異步費(fèi)阻塞通訊模型NIO2.0(AIO)
NIO非阻塞代碼
/*** 啟動(dòng)客戶端【nio異步非阻塞】* @param port 端口號(hào)* @author 墨水*/private static void upClient(int port) throws IOException{// 1.創(chuàng)建通道SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", port));// 2.切換異步非阻塞sChannel.configureBlocking(false);System.out.println("port:" + port + "客戶端已經(jīng)啟動(dòng)....");// 3.指定緩沖區(qū)大小ByteBuffer byteBuffer = ByteBuffer.allocate(1024);Scanner scanner = new Scanner(System.in);while (scanner.hasNext()){String str = scanner.next();byteBuffer.put((new Date().toString() + "\n" + str).getBytes());// 4.切換讀取模式 byteBuffer.flip();sChannel.write(byteBuffer);byteBuffer.clear();}sChannel.close();}/*** 啟動(dòng)服務(wù)端【nio異步非阻塞】* @param port 端口號(hào)* @author 墨水*/private static void upServer(int port) throws IOException, ClosedChannelException{// 1.創(chuàng)建通道ServerSocketChannel sChannel = ServerSocketChannel.open();// 2.切換讀取模式sChannel.configureBlocking(false);// 3.綁定連接sChannel.bind(new InetSocketAddress(port));// 4.獲取選擇器Selector selector = Selector.open();// 5.將通道注冊(cè)到選擇器 "并且指定監(jiān)聽(tīng)接受事件" sChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("port:" + port + "服務(wù)器端已經(jīng)啟動(dòng)....");// 6. 輪訓(xùn)式 獲取選擇 "已經(jīng)準(zhǔn)備就緒"的事件while (selector.select() > 0){// 7.獲取當(dāng)前選擇器所有注冊(cè)的"選擇鍵(已經(jīng)就緒的監(jiān)聽(tīng)事件)"Iterator<SelectionKey> it = selector.selectedKeys().iterator();while (it.hasNext()){// 8.獲取準(zhǔn)備就緒的事件SelectionKey sk = it.next();// 9.判斷具體是什么事件準(zhǔn)備就緒if (sk.isAcceptable()){// 10.若"接受就緒",獲取客戶端連接SocketChannel socketChannel = sChannel.accept();// 11.設(shè)置阻塞模式socketChannel.configureBlocking(false);// 12.將該通道注冊(cè)到服務(wù)器上 socketChannel.register(selector, SelectionKey.OP_READ);}else if (sk.isReadable()){// 13.獲取當(dāng)前選擇器"就緒"// 狀態(tài)的通道SocketChannel socketChannel = (SocketChannel) sk.channel();// 14.讀取數(shù)據(jù)ByteBuffer buf = ByteBuffer.allocate(1024);int len = 0;while ((len = socketChannel.read(buf)) > 0){buf.flip();System.out.println("port:" + port + "||||||||" + new String(buf.array(), 0, len));buf.clear();}}it.remove();}}}選擇KEY
1、SelectionKey.OP_CONNECT
2、SelectionKey.OP_ACCEPT
3、SelectionKey.OP_READ
4、SelectionKey.OP_WRITE
如果你對(duì)不止一種事件感興趣,那么可以用“位或”操作符將常量連接起來(lái),如下:
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
在SelectionKey類的源碼中我們可以看到如下的4中屬性,四個(gè)變量用來(lái)表示四種不同類型的事件:可讀、可寫(xiě)、可連接、可接受連接
?
轉(zhuǎn)載于:https://www.cnblogs.com/hongmoshui/p/10985902.html
總結(jié)
以上是生活随笔為你收集整理的【学习】009 NIO编程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Java常用工具类---IP工具类、Fi
- 下一篇: 题解 DTOJ #1438. 矮人排队(