国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Java NIO 詳解

這篇具有很好參考價(jià)值的文章主要介紹了Java NIO 詳解。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

一、NIO簡介

NIO 是 Java SE 1.4 引入的一組新的 I/O 相關(guān)的 API,它提供了非阻塞式 I/O、選擇器、通道、緩沖區(qū)等新的概念和機(jī)制。相比與傳統(tǒng)的 I/O 多出的 N 不是單純的 New,更多的是代表了 Non-blocking 非阻塞,NIO具有更高的并發(fā)性、可擴(kuò)展性以及更少的資源消耗等優(yōu)點(diǎn)。

二、NIO 與傳統(tǒng)BIO

NIO:是同步非阻塞的,服務(wù)器實(shí)現(xiàn)模式為 一個(gè)線程處理多個(gè)連接。服務(wù)端只會(huì)創(chuàng)建一個(gè)線程負(fù)責(zé)管理Selector(多路復(fù)用器),Selector(多路復(fù)用器)不斷的輪詢注冊其上的Channel(通道)中的 I/O 事件,并將監(jiān)聽到的事件進(jìn)行相應(yīng)的處理。每個(gè)客戶端與服務(wù)端建立連接時(shí)會(huì)創(chuàng)建一個(gè) SocketChannel 通道,通過 SocketChannel 進(jìn)行數(shù)據(jù)交互。
java nio,Java,java,nio,性能優(yōu)化

BIO:全稱是Blocking IO,同步阻塞式IO,是JDK1.4之前的傳統(tǒng)IO模型,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程。每當(dāng)客戶端有連接請求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理。
java nio,Java,java,nio,性能優(yōu)化
兩者主要區(qū)別如下:

  1. 阻塞和非阻塞:NIO 使用非阻塞式 I/O,而 BIO 使用阻塞式 I/O。在阻塞式 I/O 中,當(dāng)一個(gè) I/O 操作完成之前,線程會(huì)一直被阻塞,直到 I/O 操作完成;在非阻塞式 I/O 中,線程可以繼續(xù)執(zhí)行其他任務(wù),直到 I/O 操作完成并返回結(jié)果。
  2. 線程模型:NIO 中的線程模型是基于事件驅(qū)動(dòng)的,當(dāng)一個(gè) I/O 操作完成時(shí),會(huì)觸發(fā)相應(yīng)的事件通知線程處理;而在 BIO 中,每個(gè)線程都負(fù)責(zé)處理一個(gè)客戶端連接,需要不斷地輪詢客戶端的輸入輸出流,以便及時(shí)響應(yīng)客戶端的請求。
  3. 內(nèi)存消耗:NIO 中使用的緩沖區(qū)(Buffer)可以重復(fù)利用,減少了頻繁的內(nèi)存分配和回收,從而減少了內(nèi)存的消耗;而在 BIO 中,每個(gè)客戶端連接都需要單獨(dú)分配一個(gè)緩沖區(qū),容易造成內(nèi)存的浪費(fèi)。
  4. 并發(fā)性能:NIO 中使用非阻塞式 I/O,可以同時(shí)處理多個(gè)客戶端連接,從而提高了并發(fā)處理能力;而在 BIO 中,由于每個(gè)客戶端連接都需要一個(gè)線程來處理,當(dāng)連接數(shù)量增加時(shí),容易出現(xiàn)線程饑餓和資源耗盡的問題。
三、NIO 工作流程
  1. 創(chuàng)建 Selector:Selector 是 NIO 的核心組件之一,它可以同時(shí)監(jiān)聽多個(gè)通道上的 I/O 事件,并且可以通過 select() 方法等待事件的發(fā)生。
  2. 注冊 Channel:通過 Channel 的 register() 方法將 Channel 注冊到 Selector 上,這樣 Selector 就可以監(jiān)聽 Channel 上的 I/O 事件。
  3. 等待事件:調(diào)用 Selector 的 select() 方法等待事件的發(fā)生,當(dāng)有事件發(fā)生時(shí),Selector 就會(huì)通知相應(yīng)的線程進(jìn)行處理。
  4. 處理事件:根據(jù)不同的事件類型,調(diào)用對(duì)應(yīng)的處理邏輯。
  5. 關(guān)閉 Channel:當(dāng) Channel 不再需要使用時(shí),需要調(diào)用 Channel 的 close() 方法關(guān)閉 Channel,同時(shí)也需要調(diào)用 Buffer 的 clear() 方法清空 Buffer 中的數(shù)據(jù),以釋放內(nèi)存資源。

Java NIO 的工作流程可以簡單概括為:通過 Selector 監(jiān)聽多個(gè) Channel 上的 I/O 事件,當(dāng)事件發(fā)生時(shí),通過對(duì)應(yīng)的 Channel 進(jìn)行讀寫操作,并在 Channel 不再需要使用時(shí)關(guān)閉 Channel。

四、NIO 核心的組件
1. Channel(通道)

Channel 是應(yīng)用程序與操作系統(tǒng)之間交互事件和傳遞內(nèi)容的直接交互渠道,應(yīng)用程序可以從管道中讀取操作系統(tǒng)中接收到的數(shù)據(jù),也可以向操作系統(tǒng)發(fā)送數(shù)據(jù)。Channel和傳統(tǒng)IO中的Stream很相似,其主要區(qū)別為:通道是雙向的,通過一個(gè)Channel既可以進(jìn)行讀,也可以進(jìn)行寫;而Stream只能進(jìn)行單向操作,通過一個(gè)Stream只能進(jìn)行讀或者寫,比如InputStream只能進(jìn)行讀取操作,OutputStream只能進(jìn)行寫操作。

1.1 常用的Channel實(shí)現(xiàn)類
  1. FileChannel:本地文件IO通道,從文件中讀寫數(shù)據(jù)。一般流程為:
1.獲取文件通道,通過 FileChannel 的靜態(tài)方法 open() 來獲取,獲取時(shí)需要指定文件路徑和文件打開方式
FileChannel channel = FileChannel.open(Paths.get(fileName), StandardOpenOption.READ);

2.創(chuàng)建字節(jié)緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024);

3.讀/寫操作
(1)、讀操作
// 循環(huán)讀取通道中的數(shù)據(jù),并寫入到 buf 中
while (channel.read(buf) != -1){ 
    // 緩存區(qū)切換到讀模式
    buf.flip(); 
    // 讀取 buf 中的數(shù)據(jù)
    while (buf.position() < buf.limit()){ 
    	// 將buf中的數(shù)據(jù)追加到文件中
        text.append((char)buf.get());
    }
    // 清空已經(jīng)讀取完成的 buffer,以便后續(xù)使用
    buf.clear();
}
(2)、寫操作
// 循環(huán)讀取文件中的數(shù)據(jù),并寫入到 buf 中
for (int i = 0; i < text.length(); i++) {
    // 填充緩沖區(qū),需要將 2 字節(jié)的 char 強(qiáng)轉(zhuǎn)為 1 自己的 byte
    buf.put((byte)text.charAt(i)); 
    // 緩存區(qū)已滿或者已經(jīng)遍歷到最后一個(gè)字符
    if (buf.position() == buf.limit() || i == text.length() - 1) { 
        // 將緩沖區(qū)由寫模式置為讀模式
        buf.flip(); 
        // 將緩沖區(qū)的數(shù)據(jù)寫到通道
        channel.write(buf); 
        // 清空已經(jīng)讀取完成的 buffer,以便后續(xù)使用
        buf.clear(); 
    }
}

4.將數(shù)據(jù)刷出到物理磁盤
channel.force(false);

5.關(guān)閉通道
channel.close();
  1. SocketChannel:網(wǎng)絡(luò)套接字IO通道,TCP協(xié)議,客戶端通過 SocketChannel 與服務(wù)端建立TCP連接進(jìn)行通信交互。與傳統(tǒng)的Socket操作不同的是,SocketChannel基于非阻塞IO模式,可以在同一個(gè)線程內(nèi)同時(shí)管理多個(gè)通信連接,從而提高系統(tǒng)的并發(fā)處理能力。
1.打開一個(gè) SocketChannel 通道
SocketChannel channel = SocketChannel.open();

2.連接到服務(wù)端
channel.connect(new InetSocketAddress("localhost", 9001));

3.分配緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024); 

4.配置是否為阻塞方式(默認(rèn)為阻塞方式)
channel.configureBlocking(false); // 配置通道為非阻塞模式

5.將channel的連接、讀、寫等事件注冊到selector中,每個(gè)chanel只能注冊一個(gè)事件,最后注冊的一個(gè)生效,
同時(shí)注冊多個(gè)事件可以使用"|"操作符將常量連接起來
Selector selector = Selector.open();
channel.register(selector, SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE | SelectionKey.OP_READ);

6.與服務(wù)端進(jìn)行讀寫操作
channel.read(buf);
channel.write(buf);

7.關(guān)閉通道
channel.close();
  1. ServerSocketChannel:網(wǎng)絡(luò)套接字IO通道,TCP協(xié)議,服務(wù)端通過ServerSocketChannel監(jiān)聽來自客戶端的連接請求,并創(chuàng)建相應(yīng)的SocketChannel對(duì)象進(jìn)行通信交互。ServerSocketChannel同樣也是基于非阻塞IO模式,可以在同一個(gè)線程內(nèi)同時(shí)管理多個(gè)通信連接,從而提高系統(tǒng)的并發(fā)處理能力。
1.打開一個(gè) ServerSocketChannel 通道
ServerSocketChannel serverChannel = ServerSocketChannel.open();

2.綁定本地端口
serverChannel.bind(new InetSocketAddress(9001));

3.配置是否為阻塞方式(默認(rèn)為阻塞方式)
serverChannel.configureBlocking(false); // 配置通道為非阻塞模式

4.分配緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024); 

5.將serverChannel 的連接、讀、寫等事件注冊到selector中,每個(gè)chanel只能注冊一個(gè)事件,最后注冊的一個(gè)生效,
同時(shí)注冊多個(gè)事件可以使用"|"操作符將常量連接起來
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT| SelectionKey.OP_WRITE | SelectionKey.OP_READ);

6.與客服端進(jìn)行讀寫操作
serverChannel.read(buf);
serverChannel.write(buf);

7.關(guān)閉通道
serverChannel.close();
  1. DatagramChannel:DatagramChannel是Java NIO中對(duì)UDP協(xié)議通信的封裝。通過DatagramChannel對(duì)象,我們可以實(shí)現(xiàn)發(fā)送和接收UDP數(shù)據(jù)包。它與TCP協(xié)議不同的是,UDP協(xié)議沒有連接的概念,所以無需像SocketChannel一樣先建立連接再開始通信。
1.打開一個(gè) DatagramChannel 通道
DatagramChannel channel = DatagramChannel.open();

2.分配緩沖區(qū)
ByteBuffer buf = ByteBuffer.allocate(1024); 

3.配置是否為阻塞方式(默認(rèn)為阻塞方式)
channel.configureBlocking(false); // 配置通道為非阻塞模式

4.與客服端進(jìn)行讀寫操作
buffer.flip();
// 發(fā)送消息給服務(wù)端
channel.send(buffer, new InetSocketAddress("localhost", 9001));
buffer.clear();
// 接收服務(wù)端的響應(yīng)信息
channel.receive(buffer);
buffer.flip();
// 打印出響應(yīng)信息
while (buffer.hasRemaining()) {
     System.out.print((char) buffer.get());
}
buffer.clear();

7.關(guān)閉通道
channel.close();
1.2 常用的Channel方法
  1. read(ByteBuffer):從 Channel 中讀取數(shù)據(jù)到 ByteBuffer 中。如果 Channel 中沒有可讀數(shù)據(jù),則會(huì)阻塞等待直到有數(shù)據(jù)可讀。
  2. write(ByteBuffer):將數(shù)據(jù)寫入到 Channel 中。如果 Channel 中沒有可寫空間,則會(huì)阻塞等待直到有可寫空間。
  3. read(ByteBuffer, long):從 Channel 中讀取數(shù)據(jù)到 ByteBuffer 中,并設(shè)置讀取超時(shí)時(shí)間。如果超時(shí)時(shí)間到了還沒有讀取到數(shù)據(jù),則會(huì)拋出 TimeoutException 異常。
  4. write(ByteBuffer, long):將數(shù)據(jù)寫入到 Channel 中,并設(shè)置寫入超時(shí)時(shí)間。如果超時(shí)時(shí)間到了還沒有寫入完成,則會(huì)拋出 TimeoutException 異常。
  5. flush():將 Channel 中的緩沖區(qū)數(shù)據(jù)刷新到底層設(shè)備中,如果沒有數(shù)據(jù)需要刷新,則會(huì)立即返回。
  6. register(SelectionKey, int):將 Channel 注冊到 Selector 上,并設(shè)置注冊的事件類型和操作??梢酝ㄟ^ Selector 監(jiān)聽 Channel 上的事件,當(dāng)有事件發(fā)生時(shí),Selector 就會(huì)通知相應(yīng)的線程進(jìn)行處理。
  7. configureBlocking(boolean):設(shè)置 Channel 是否為阻塞模式。如果為阻塞模式,則在讀取或?qū)懭霐?shù)據(jù)時(shí)會(huì)一直阻塞等待,直到有數(shù)據(jù)可讀或?qū)懭胪瓿桑蝗绻麨榉亲枞J?,則在讀取或?qū)懭霐?shù)據(jù)時(shí)會(huì)立即返回,如果沒有數(shù)據(jù)可讀或?qū)懭胪瓿?,則會(huì)返回 -1。
  8. socket():獲取底層的 Socket 對(duì)象。
  9. isConnected():判斷 Channel 是否已經(jīng)連接到了遠(yuǎn)程主機(jī)。
  10. isWritable():判斷 Channel 是否可以寫入數(shù)據(jù)。
  11. isReadable():判斷 Channel 是否可以讀取數(shù)據(jù)。
  12. isOpen():檢查 Channel 是否已經(jīng)打開。
  13. getRemoteAddress():獲取 Channel 對(duì)應(yīng)的遠(yuǎn)程地址。
  14. getLocalAddress():獲取 Channel 對(duì)應(yīng)的本地地址。
2. Buffer(緩沖區(qū))

NIO 中的數(shù)據(jù)都是通過 Buffer 對(duì)象來處理的,每個(gè) Buffer 對(duì)象都關(guān)聯(lián)著一個(gè)字節(jié)數(shù)組,可以保存多個(gè)相同類型的數(shù)據(jù)。在讀取數(shù)據(jù)時(shí),是從Buffer 中讀取的,在寫入數(shù)據(jù)時(shí),也是寫入到 Buffer 中的。
java nio,Java,java,nio,性能優(yōu)化

2.1 Buffer 常用子類
  1. ByteBuffer:用于存儲(chǔ)字節(jié)數(shù)據(jù);
  2. CharBuffer:用于存儲(chǔ)字符數(shù)據(jù);
  3. ShortBuffer:用于存儲(chǔ)Short類型數(shù)據(jù);
  4. IntBuffer:用于存儲(chǔ)Int類型數(shù)據(jù);
  5. LongBuffer:用于存儲(chǔ)Long類型數(shù)據(jù);
  6. FloatBuffer:用于存儲(chǔ)Float類型數(shù)據(jù);
  7. DoubleBuffer:用于存儲(chǔ)Double類型數(shù)據(jù);
2.2 Buffer 重要屬性
  1. capacity(容量):表示 Buffer 所占的內(nèi)存大小,capacity不能為負(fù),并且創(chuàng)建后不能更改。
  2. limit(限制):表示 Buffer 中可以操作數(shù)據(jù)的大小,limit不能為負(fù),并且不能大于其capacity。寫模式下,表示最多能往 Buffer 里寫多少數(shù)據(jù),即 limit 等于 Buffer 的capacity。讀模式下,表示你最多能讀到多少數(shù)據(jù),其實(shí)就是能讀到之前寫入的所有數(shù)據(jù)。
  3. position(位置):表示下一個(gè)要讀取或?qū)懭氲臄?shù)據(jù)的索引。緩沖區(qū)的位置不能為負(fù),并且不能大于其限制。初始的 position 值為 0,最大可為 capacity – 1。當(dāng)一個(gè) byte、long 等數(shù)據(jù)寫到 Buffer 后, position 會(huì)向前移動(dòng)到下一個(gè)可插入數(shù)據(jù)的 Buffer 單元。
  4. mark(標(biāo)記):表示記錄當(dāng)前 position 的位置。可以通過 reset() 恢復(fù)到 mark 的位置。
    java nio,Java,java,nio,性能優(yōu)化
2.3 Buffer 常見方法
  1. clear():清空緩沖區(qū)并返回對(duì)緩沖區(qū)的引用;
  2. flip():將緩沖區(qū)的界限設(shè)置為當(dāng)前位置,并將當(dāng)前位置重置為 0;
  3. capacity():返回 Buffer 的 capacity 大??;
  4. limit():返回 Buffer 的界限(limit) 的位置;
  5. limit(int n):將設(shè)置緩沖區(qū)界限為 n,并返回一個(gè)具有新 limit 的緩沖區(qū)對(duì)象;
  6. position():返回緩沖區(qū)的當(dāng)前位置 position;
  7. position(int n):將設(shè)置緩沖區(qū)的當(dāng)前位置為 n, 并返回修改后的 Buffer 對(duì)象;
  8. mark():對(duì)緩沖區(qū)設(shè)置標(biāo)記;
  9. reset():將位置 position 轉(zhuǎn)到以前設(shè)置的mark 所在的位置;
  10. rewind():將位置設(shè)為為 0, 取消設(shè)置的 mark;
  11. hasRemaining():判斷緩沖區(qū)中是否還有元素;
  12. get():讀取單個(gè)字節(jié);
  13. get(byte[] dst):讀取多個(gè)字節(jié);
  14. get(int index):讀取指定索引位置的字節(jié);
  15. put(byte b):將給定單個(gè)字節(jié)寫入緩沖區(qū)的當(dāng)前位置;
  16. put(byte[] src):將數(shù)組中的字節(jié)從當(dāng)前位置依次寫入到緩沖區(qū)中;
  17. put(int index, byte b):將指定字節(jié)寫入緩沖區(qū)的索引位置;
2.4 Buffer 內(nèi)存分配
  1. 普通緩沖區(qū):通過allocate()方法進(jìn)行分配,可以在jvm堆上申請堆上內(nèi)存。如果要作IO操作,會(huì)先從本進(jìn)程的堆上內(nèi)存復(fù)制到直接內(nèi)存,再利用本地IO處理。
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
  1. 直接緩沖區(qū):通過allocateDirect()方法進(jìn)行分配,直接從本地內(nèi)存中申請。如果要作IO操作,直接從本地內(nèi)存中利用本地IO處理。使用直接內(nèi)存會(huì)具有更高的效率,但是它比申請普通的堆內(nèi)存需要耗費(fèi)更高的性能。直接內(nèi)存中的數(shù)據(jù)是在JVM之外的,因此它不會(huì)占用應(yīng)用的內(nèi)存,當(dāng)有很大的數(shù)據(jù)要緩存,并且它的生命周期又很長,那么就比較適合使用直接內(nèi)存。一般來說,如果不是能帶來很明顯的性能提升,還是推薦使用堆內(nèi)存。
ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(1024);
  1. 緩沖區(qū)分片:通過slice()方法可以根據(jù)現(xiàn)有的緩沖區(qū)對(duì)象來創(chuàng)建一個(gè)子緩沖區(qū),即在現(xiàn)有緩沖區(qū)上切出一片來作為一個(gè)新的緩沖區(qū),但現(xiàn)有的緩沖區(qū)與創(chuàng)建的子緩沖區(qū)在底層數(shù)組層面上是數(shù)據(jù)共享的。
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
ByteBuffer sliceBuffer = readBuffer.slice();
  1. 只讀緩沖區(qū):通過asReadOnlyBuffer()方法可以將任何常規(guī)緩沖區(qū)轉(zhuǎn)換為只讀緩沖區(qū),這個(gè)方法返回 一個(gè)與原緩沖區(qū)完全相同的緩沖區(qū),并與原緩沖區(qū)共享數(shù)據(jù),只不過它是只讀的。如果原緩沖區(qū)的內(nèi)容發(fā)生了變化,只讀緩沖區(qū)的內(nèi)容也隨之發(fā)生變化。
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
ByteBuffer readonlyBuffer = readBuffer.asReadOnlyBuffer();
3. Selector(選擇器)

Selector 提供了選擇已經(jīng)就緒的任務(wù)的能力,Selector會(huì)不斷的輪詢注冊在上面的所有channel,進(jìn)行后續(xù)的IO操作。只需通過一個(gè)單獨(dú)的線程就可以管理多個(gè)channel,從而管理多個(gè)網(wǎng)絡(luò)連接。這就是Nio與傳統(tǒng)I/O最大的區(qū)別,不用為每個(gè)連接都去創(chuàng)建一個(gè)線程。
java nio,Java,java,nio,性能優(yōu)化

3.1 Selector使用流程
1.獲取選擇器
Selector selector = Selector.open();

2.通道注冊到選擇器,進(jìn)行監(jiān)聽
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

3.獲取可操作的 Channel
selector.select();

4.獲取可操作的 Channel 中的就緒事件集合
Set<SelectionKey> keys = selector.selectedKeys();

5.處理就緒事件
while (keys.iterator().hasNext()){
	SelectionKey key = keys.iterator().next();
	if (!key.isValid()){
		continue;
	}
    if (key.isAcceptable()){
		accept(key);
	}
	if(key.isReadable()){
		read(key);
	}
	if (key.isWritable()){
		write(key);
	}
	keyIterator.remove(); //移除當(dāng)前的key
}
3.2 SelectionKey事件類型

每個(gè) Channel向Selector 注冊時(shí),都會(huì)創(chuàng)建一個(gè) SelectionKey 對(duì)象,通過 SelectionKey 對(duì)象向Selector 注冊,且 SelectionKey 中維護(hù)了 Channel 的事件。常見的四種事件如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-849229.html

  1. OP_READ:當(dāng)操作系統(tǒng)讀緩沖區(qū)有數(shù)據(jù)可讀時(shí)就緒。
  2. OP_WRITE:當(dāng)操作系統(tǒng)寫緩沖區(qū)有空閑空間時(shí)就緒。
  3. OP_CONNECT:當(dāng) SocketChannel.connect()請求連接成功后就緒,該操作只給客戶端使用。
  4. OP_ACCEPT:當(dāng)接收到一個(gè)客戶端連接請求時(shí)就緒,該操作只給服務(wù)器使用。
五、簡單實(shí)例
1. 服務(wù)端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioServiceTest {
    private Selector selector;
    private ServerSocketChannel serverSocketChannel;
    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);//調(diào)整緩沖區(qū)大小為1024字節(jié)
    private ByteBuffer sendBuffer = ByteBuffer.allocate(1024);
    String str;

    public NioServiceTest(int port) throws IOException {
        // 打開服務(wù)器套接字通道
        this.serverSocketChannel = ServerSocketChannel.open();
        // 服務(wù)器配置為非阻塞 即異步IO
        this.serverSocketChannel.configureBlocking(false);
        // 綁定本地端口
        this.serverSocketChannel.bind(new InetSocketAddress(port));
        // 創(chuàng)建選擇器
        this.selector = Selector.open();
        // 注冊接收連接事件
        this.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    public void handle() throws IOException {
        // 無限判斷當(dāng)前線程狀態(tài),如果沒有中斷,就一直執(zhí)行while內(nèi)容。
        while(!Thread.currentThread().isInterrupted()){
            // 獲取準(zhǔn)備就緒的channel
            if (selector.select() == 0) {
                continue;
            }

            // 獲取到對(duì)應(yīng)的 SelectionKey 對(duì)象
            Set<SelectionKey> keys = selector.selectedKeys();
            Iterator<SelectionKey> keyIterator = keys.iterator();
            // 遍歷所有的 SelectionKey 對(duì)象
            while (keyIterator.hasNext()){
                // 根據(jù)不同的SelectionKey事件類型進(jìn)行相應(yīng)的處理
                SelectionKey key = keyIterator.next();
                if (!key.isValid()){
                    continue;
                }
                if (key.isAcceptable()){
                    accept(key);
                }
                if(key.isReadable()){
                    read(key);
                }
                // 移除當(dāng)前的key
                keyIterator.remove();
            }
        }
    }

    /**
     * 客服端連接事件處理
     *
     * @param key
     * @throws IOException
     */
    private void accept(SelectionKey key) throws IOException {
        SocketChannel socketChannel = this.serverSocketChannel.accept();
        socketChannel.configureBlocking(false);
        // 注冊客戶端讀取事件到selector
        socketChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("client connected " + socketChannel.getRemoteAddress());
    }

    /**
     * 讀取事件處理
     *
     * @param key
     * @throws IOException
     */
    private void read(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
        //清除緩沖區(qū),準(zhǔn)備接受新數(shù)據(jù)
        this.readBuffer.clear();
        int numRead;
        try{
            // 從 channel 中讀取數(shù)據(jù)
            numRead = socketChannel.read(this.readBuffer);
        }catch (IOException e){
            System.out.println("read failed");
            key.cancel();
            socketChannel.close();
            return;
        }
        str = new String(readBuffer.array(),0,numRead);
        System.out.println("read String is: " + str);
    }

    public static void main(String[] args) throws Exception {
        System.out.println("sever start...");
        new NioServiceTest(8000).handle();
    }
}
2. 客戶端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set;

public class NioClientTest {
    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
    private SocketChannel sc;
    private Selector selector;

    public NioClientTest(String hostname, int port) throws IOException {
        // 打開socket通道
        sc = SocketChannel.open();
        // 配置為非阻塞 即異步IO
        sc.configureBlocking(false);
        // 連接服務(wù)器端
        sc.connect(new InetSocketAddress(hostname,port));
        // 創(chuàng)建選擇器
        selector = Selector.open();
        // 注冊請求連接事件
        sc.register(selector, SelectionKey.OP_CONNECT);
    }

    public void send() throws IOException{
        Scanner scanner = new Scanner(System.in);
        // 無限判斷當(dāng)前線程狀態(tài),如果沒有中斷,就一直執(zhí)行while內(nèi)容。
        while (!Thread.currentThread().isInterrupted()){
            // 獲取準(zhǔn)備就緒的channel
            if (selector.select() == 0) {
                continue;
            }

            // 獲取到對(duì)應(yīng)的 SelectionKey 對(duì)象
            Set<SelectionKey> keys = selector.selectedKeys();
            System.out.println("all keys is:"+keys.size());
            Iterator<SelectionKey> iterator = keys.iterator();
            // 遍歷所有的 SelectionKey 對(duì)象
            while (iterator.hasNext()){
                SelectionKey key = iterator.next();
                //判斷此通道上是否在進(jìn)行連接操作
                if (key.isConnectable()){
                    sc.finishConnect();
                    //注冊寫操作
                    sc.register(selector, SelectionKey.OP_WRITE);
                    System.out.println("server connected...");
                    break;
                }else if (key.isWritable()){
                    System.out.println("please input message:");
                    String message = scanner.nextLine();
                    writeBuffer.clear();
                    writeBuffer.put(message.getBytes());
                    //將緩沖區(qū)各標(biāo)志復(fù)位,因?yàn)橄蚶锩鎝ut了數(shù)據(jù)標(biāo)志被改變要想從中讀取數(shù)據(jù)發(fā)向服務(wù)器,就要復(fù)位
                    writeBuffer.flip();
                    sc.write(writeBuffer);
                }
                // 移除當(dāng)前的key
                iterator.remove();
            }
        }
    }

    public static void main(String[] args) throws Exception {
        new NioClientTest("localhost", 8000).send();
    }
}

到了這里,關(guān)于Java NIO 詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Java-NIO篇章(3)——Channel通道類詳解

    Java NIO中,一個(gè)socket連接使用一個(gè)Channel(通道)來表示。對(duì)應(yīng)到不同的網(wǎng)絡(luò)傳輸協(xié)議類型,在Java中都有不同的NIO Channel(通道) 相對(duì)應(yīng)。其中最為重要的四種Channel(通道)實(shí)現(xiàn): FileChannel、 SocketChannel、 ServerSocketChannel、 DatagramChannel : FileChannel 文件通道,用于文件的數(shù)據(jù)讀

    2024年01月20日
    瀏覽(24)
  • Java-NIO篇章(4)——Selector選擇器詳解

    Java-NIO篇章(4)——Selector選擇器詳解

    選擇器(Selector)是什么呢?選擇器和通道的關(guān)系又是什么?這里詳細(xì)說明,假設(shè)不用選擇器,那么一個(gè)客戶端請求數(shù)據(jù)傳輸那就需要建立一個(gè)連接,為了避免線程阻塞,那么每個(gè)客戶端開辟一個(gè)線程。而學(xué)過JVM的都知道,默認(rèn)每開一個(gè)線程需要??臻g內(nèi)存1MB大小。如果這時(shí)

    2024年01月21日
    瀏覽(31)
  • Java NIO (三)NIO Channel類

    Java NIO (三)NIO Channel類

    ? ? ? ? 前面提到,Java NIO中一個(gè)socket連接使用一個(gè)Channel來表示。從更廣泛的層面來說,一個(gè)通道可以表示一個(gè)底層的文件描述符,例如硬件設(shè)備、文件、網(wǎng)絡(luò)連接等。然而,遠(yuǎn)不止如此,Java NIO的通道可以更加細(xì)化。例如,不同的網(wǎng)絡(luò)傳輸協(xié)議,在Java中都有不同的NIO Chann

    2024年01月18日
    瀏覽(47)
  • JAVA NIO簡解

    JAVA NIO簡解

    Java nio是Java的一個(gè)新的輸入輸出(NewInput/Output)API,它提供了一些高效的數(shù)據(jù)處理方式,如緩沖區(qū)(buffers)、字符集(charsets)、通道(channels)和選擇器(selectors)。 Java NIO可以實(shí)現(xiàn)非阻塞式的多路復(fù)用輸入輸出,提高了程序的性能和可擴(kuò)展性。Java nio是在Java 1.4版本中引入

    2023年04月16日
    瀏覽(20)
  • Java NIO

    Java NIO

    在傳統(tǒng)的 Java I/O 模型(BIO)中,I/O 操作是以阻塞的方式進(jìn)行的。也就是說, 當(dāng)一個(gè)線程執(zhí)行一個(gè) I/O 操作時(shí),它會(huì)被阻塞直到操作完成 。這種阻塞模型在處理多個(gè)并發(fā)連接時(shí)可能會(huì)導(dǎo)致性能瓶頸,因?yàn)樾枰獮槊總€(gè)連接創(chuàng)建一個(gè)線程,而線程的創(chuàng)建和切換都是有開銷的。 為了

    2024年01月25日
    瀏覽(21)
  • java nio零拷貝

    java nio零拷貝

    ? 零拷貝是一種計(jì)算機(jī)執(zhí)行IO操作的優(yōu)化技術(shù),其核心目標(biāo)是減少數(shù)據(jù)拷貝次數(shù),從而提高系統(tǒng)性能。它主要體現(xiàn)在以下幾個(gè)方面: 1. **定義與原理**:零拷貝字面上的意思包括“零”和“拷貝”。其中,“拷貝”是指數(shù)據(jù)從一個(gè)存儲(chǔ)區(qū)域轉(zhuǎn)移到另一個(gè)存儲(chǔ)區(qū)域;“零”表示次

    2024年02月20日
    瀏覽(18)
  • Java NIO (一)簡介

    Java NIO (一)簡介

    ? ? ? ? 在1.4版本之前,Java NIO類庫是阻塞IO,從1.4版本開始,引進(jìn)了新的異步IO庫,被稱為Java New IO類庫,簡稱為Java NIO。New IO類庫的目的 就是要讓Java支持非阻塞IO。 ? ? ? ? Java NIO類庫包含三個(gè)核心組件: ? ? ? ? 1、Channel(通道) ? ? ? ? 2、Buffer(緩沖區(qū)) ? ? ? ? 3、

    2024年01月20日
    瀏覽(15)
  • 淺談Java NIO

    淺談Java NIO

    Java NIO(New Input/Output)是Java平臺(tái)上一種全新的IO API,相較于傳統(tǒng)的Java I/O(也稱為BIO,阻塞I/O),NIO引入了非阻塞I/O和通道(Channel)的概念,以及緩沖區(qū)(Buffer)和選擇器(Selector)機(jī)制,大大提升了系統(tǒng)的并發(fā)處理能力和性能。以下是對(duì)NIO實(shí)現(xiàn)原理的關(guān)鍵點(diǎn)概述: 非阻塞

    2024年04月17日
    瀏覽(17)
  • BIO、NIO、IO多路復(fù)用模型詳細(xì)介紹&Java NIO 網(wǎng)絡(luò)編程

    BIO、NIO、IO多路復(fù)用模型詳細(xì)介紹&Java NIO 網(wǎng)絡(luò)編程

    上文介紹了網(wǎng)絡(luò)編程的基礎(chǔ)知識(shí),并基于 Java 編寫了 BIO 的網(wǎng)絡(luò)編程。我們知道 BIO 模型是存在巨大問題的,比如 C10K 問題,其本質(zhì)就是因其阻塞原因,導(dǎo)致如果想要承受更多的請求就必須有足夠多的線程,但是足夠多的線程會(huì)帶來內(nèi)存占用問題、CPU上下文切換帶來的性能問題

    2024年02月14日
    瀏覽(29)
  • Java-NIO介紹

    Java NIO(New IO)是Java提供的一種用于進(jìn)行高效IO操作的API。NIO庫允許開發(fā)人員使用非阻塞、事件驅(qū)動(dòng)的方式進(jìn)行IO操作,從而提高程序的性能和可擴(kuò)展性。 相對(duì)于傳統(tǒng)的Java IO(InputStream/OutputStream)來說,Java NIO引入了以下幾個(gè)新的概念和組件: 通道(Channel):數(shù)據(jù)源和目的地

    2024年02月11日
    瀏覽(19)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包