Java中的同步非阻塞IO模型詳解
介紹
IO模型中的,同步非阻塞IO模型,我們能夠知道,用戶線程一直發(fā)送請求,內(nèi)核一直都能都夠返回 ,直到內(nèi)核完成準(zhǔn)備數(shù)據(jù)、數(shù)據(jù)拷貝的工作。
并且返回成功的指示。在此過程中用戶線程不是阻塞的狀態(tài)
實現(xiàn)
使用java來實現(xiàn)同步非阻塞IO模型,即我們所說的NIO的模型
客戶端實現(xiàn)
/**
* @author:triumphxx
* @Date:2021/11/12
* @Time:11:03 下午
* @微信公眾號:北漂碼農(nóng)有話說
* @網(wǎng)站:http://blog.triumphxx.com.cn
* @GitHub https://github.com/triumphxx
* @Desc: NIO編程模型客戶端
**/
public class NIOClient {
// 通道管理器(Selector)
private static Selector selector;
public static void main(String[] args) throws IOException {
// 創(chuàng)建通道管理器(Selector)
selector = Selector.open();
// 創(chuàng)建通道SocketChannel
SocketChannel channel = SocketChannel.open();
// 將通道設(shè)置為非阻塞
channel.configureBlocking(false);
// 客戶端連接服務(wù)器,其實方法執(zhí)行并沒有實現(xiàn)連接,需要在handleConnect方法中調(diào)channel.finishConnect()才能完成連接
channel.connect(new InetSocketAddress("localhost", 9090));
/**
* 將通道(Channel)注冊到通道管理器(Selector),并為該通道注冊selectionKey.OP_CONNECT
* 注冊該事件后,當(dāng)事件到達(dá)的時候,selector.select()會返回,
* 如果事件沒有到達(dá)selector.select()會一直阻塞。
*/
channel.register(selector, SelectionKey.OP_CONNECT);
// 循環(huán)處理
while (true) {
/*
* 選擇一組可以進(jìn)行I/O操作的事件,放在selector中,客戶端的該方法不會阻塞,
* selector的wakeup方法被調(diào)用,方法返回,而對于客戶端來說,通道一直是被選中的
* 這里和服務(wù)端的方法不一樣,查看api注釋可以知道,當(dāng)至少一個通道被選中時。
*/
selector.select();
// 獲取監(jiān)聽事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 迭代處理
while (iterator.hasNext()) {
// 獲取事件
SelectionKey key = iterator.next();
// 移除事件,避免重復(fù)處理
iterator.remove();
// 檢查是否是一個就緒的已經(jīng)連接服務(wù)端成功事件
if (key.isConnectable()) {
handleConnect(key);
} else if (key.isReadable()) {// 檢查套接字是否已經(jīng)準(zhǔn)備好讀數(shù)據(jù)
handleRead(key);
}
}
}
}
/**
* 處理客戶端連接服務(wù)端成功事件
*/
private static void handleConnect(SelectionKey key) throws IOException {
// 獲取與服務(wù)端建立連接的通道
SocketChannel channel = (SocketChannel) key.channel();
if (channel.isConnectionPending()) {
// channel.finishConnect()才能完成連接
channel.finishConnect();
}
channel.configureBlocking(false);
// 數(shù)據(jù)寫入通道
String msg = "Hello Server!";
channel.write(ByteBuffer.wrap(msg.getBytes()));
// 通道注冊到選擇器,并且這個通道只對讀事件感興趣
channel.register(selector, SelectionKey.OP_READ);
}
/**
* 監(jiān)聽到讀事件,讀取客戶端發(fā)送過來的消息
*/
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
//開辟堆內(nèi)緩沖區(qū)
ByteBuffer buffer = ByteBuffer.allocate(128);
// 從通道讀取數(shù)據(jù)到緩沖區(qū)
channel.read(buffer);
// 輸出服務(wù)端響應(yīng)發(fā)送過來的消息
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("服務(wù)端發(fā)來的消息:" + msg);
}
}由客戶端的代碼可以知道:首先我們將 select 打開,然后創(chuàng)建通道、將通道設(shè)置為非阻塞、創(chuàng)建連接、完成注冊 等待監(jiān)聽、處理事件,詳細(xì)的每一行代碼的意思,都已經(jīng)進(jìn)展標(biāo)注??创a即可。
服務(wù)端實現(xiàn)
**
* @author:triumphxx
* @Date:2021/11/12
* @Time:11:03 下午
* @微信公眾號:北漂碼農(nóng)有話說
* @網(wǎng)站:http://blog.triumphxx.com.cn
* @GitHub https://github.com/triumphxx
* @Desc: NIO編程模型服務(wù)端
**/
public class NIOServer {
// 通道管理器(Selector)
private static Selector selector;
public static void main(String[] args) throws IOException {
// 創(chuàng)建通道管理器(Selector)
selector = Selector.open();
// 創(chuàng)建通道ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 將通道設(shè)置為非阻塞
serverSocketChannel.configureBlocking(false);
// 將ServerSocketChannel對應(yīng)的ServerSocket綁定到指定端口(port)
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(9090));
/**
* 將通道(Channel)注冊到通道管理器(Selector),并為該通道注冊selectionKey.OP_ACCEPT事件
* 注冊該事件后,當(dāng)事件到達(dá)的時候,selector.select()會返回,
* 如果事件沒有到達(dá)selector.select()會一直阻塞。
*/
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 循環(huán)處理
while (true) {
// 當(dāng)注冊事件到達(dá)時,方法返回,否則該方法會一直阻塞
selector.select();
// 獲取監(jiān)聽事件
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
// 迭代處理
while (iterator.hasNext()) {
// 獲取事件
SelectionKey key = iterator.next();
// 移除事件,避免重復(fù)處理
iterator.remove();
// 檢查是否是一個就緒的可以被接受的客戶端請求連接
if (key.isAcceptable()) {
handleAccept(key);
} else if (key.isReadable()) {// 檢查套接字是否已經(jīng)準(zhǔn)備好讀數(shù)據(jù)
handleRead(key);
}
}
}
}
/**
* 處理客戶端連接成功事件
*/
private static void handleAccept(SelectionKey key) throws IOException {
// 獲取客戶端連接通道
ServerSocketChannel server = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = server.accept();
socketChannel.configureBlocking(false);
// 信息通過通道發(fā)送給客戶端
String msg = "Hello Client!";
socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
// 給通道設(shè)置讀事件,客戶端監(jiān)聽到讀事件后,進(jìn)行讀取操作
socketChannel.register(selector, SelectionKey.OP_READ);
}
/**
* 監(jiān)聽到讀事件,讀取客戶端發(fā)送過來的消息
*/
private static void handleRead(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
// 從通道讀取數(shù)據(jù)到緩沖區(qū)
ByteBuffer buffer = ByteBuffer.allocate(128);
channel.read(buffer);
// 輸出客戶端發(fā)送過來的消息
byte[] data = buffer.array();
String msg = new String(data).trim();
System.out.println("server received msg from client:" + msg);
}
}有服務(wù)端的代碼可以知道:首先我們將 select 打開,然后創(chuàng)建通道、將通道設(shè)置為非阻塞、創(chuàng)建連接、完成注冊 等待監(jiān)聽、處理事件,詳細(xì)的每一行代碼的意思,都已經(jīng)進(jìn)展標(biāo)注??创a即可。
優(yōu)點及缺點
優(yōu)點:可靠性高,吞吐量比較高、非阻塞
缺點:編程模型做復(fù)雜
適用場景
適用于連接比較多且連接比較短的場景。
比如聊天室!
到此這篇關(guān)于Java中的同步非阻塞IO模型詳解的文章就介紹到這了,更多相關(guān)同步非阻塞IO模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
eclipse下搭建hibernate5.0環(huán)境的步驟(圖文)
這篇文章主要介紹了eclipse下搭建hibernate5.0環(huán)境的步驟(圖文),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-05-05
Java?如何用二維數(shù)組創(chuàng)建空心菱形
這篇文章主要介紹了Java?如何用二維數(shù)組創(chuàng)建空心菱形,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
SpringBoot整合WebSocket實現(xiàn)實時通信功能
在當(dāng)今互聯(lián)網(wǎng)時代,實時通信已經(jīng)成為了許多應(yīng)用程序的基本需求,而WebSocket作為一種全雙工通信協(xié)議,為開發(fā)者提供了一種簡單、高效的實時通信解決方案,本文將介紹如何使用SpringBoot框架來實現(xiàn)WebSocket的集成,快速搭建實時通信功能,感興趣的朋友可以參考下2023-11-11
java idea如何根據(jù)請求路徑url自動找到對應(yīng)controller方法插件
這篇文章主要介紹了java idea如何根據(jù)請求路徑url自動找到對應(yīng)controller方法插件,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
詳解Alibaba?Java診斷工具Arthas查看Dubbo動態(tài)代理類
這篇文章主要介紹了Alibaba?Java診斷工具Arthas查看Dubbo動態(tài)代理類?,它可以幫助我們查看JDK或者javassist生成的動態(tài)代理類,當(dāng)然,它的功能遠(yuǎn)不止此,還可以在生產(chǎn)環(huán)境進(jìn)行診斷,需要的朋友可以參考下2022-04-04
Java數(shù)據(jù)結(jié)構(gòu)實現(xiàn)二維數(shù)組與稀疏數(shù)組轉(zhuǎn)換詳解
稀疏數(shù)組是用于優(yōu)化,壓縮具有以下特點的二維數(shù)組:當(dāng)二維數(shù)組中的元素大部分相同,有意義的數(shù)據(jù)元素較少時,可以使用稀疏數(shù)組進(jìn)行簡化,節(jié)省存儲空間2021-10-10
JavaScript Date時間對象的常用操作方法總結(jié)
本文介紹了JavaScript中Date對象的常用操作方法,包括創(chuàng)建、獲取、設(shè)置本地及UTC時間等,幫助開發(fā)者靈活處理日期與時間的相關(guān)需求,需要的朋友可以參考下2025-10-10

