java的三種IO模型詳解(BIO、NIO、AIO)
一、BIO 阻塞式 IO(Blocking IO)
每個客戶端連接都會在一個獨立的線程中處理,并且這個線程在處理 IO 操作時會阻塞,直到操作完成。
- 每個連接都需要一個獨立的線程,連接數(shù)較多時,會消耗大量的內(nèi)存和 CPU 資源
- 線程在處理 IO 操作時會阻塞
1.1、BIO 工作機制
- 客戶端通過 Socket 對象與服務(wù)端建立連接,從 Socket 中得到字節(jié)輸入流或輸出流進行數(shù)據(jù)讀寫。
- 服務(wù)端通過
ServerSocket注冊端口,調(diào)用accept方法監(jiān)聽客戶端 Socket 請求,從 Socket 中得到字節(jié)輸入流或輸出流進行數(shù)據(jù)讀寫。
1.2、BIO 實現(xiàn)單發(fā)單收
客戶端:
public static void main(String[] args) {
Socket socket = null;
try {
//與服務(wù)端連接
socket = new Socket("127.0.0.1", 5000);
//從 socket 管道中獲取字節(jié)輸出流
OutputStream os = socket.getOutputStream();
//將字節(jié)輸出流包裝為打印流
PrintStream ps = new PrintStream(os);
//發(fā)一行數(shù)據(jù)
ps.println("Hi BIO! 與服務(wù)端通信成功");
ps.flush();
} catch (IOException e) {
e.printStackTrace();
}
}服務(wù)端:
public static void main(String[] args) {
System.out.println("===服務(wù)端啟動===");
ServerSocket serverSocket = null;
try {
//注冊端口
serverSocket = new ServerSocket(5000);
//監(jiān)聽客戶端請求
Socket socket = serverSocket.accept();
//從 socket 管道中獲取字節(jié)輸入流
InputStream is = socket.getInputStream();
//將字節(jié)輸入流包裝為緩沖字符輸入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String msg;
//讀一行數(shù)據(jù)
if ((msg = br.readLine()) != null) {
System.out.println("服務(wù)端接收客戶端信息為:" + msg);
}
}catch (Exception e){
System.out.println(e.getMessage());
}
}1.3、BIO 實現(xiàn)多發(fā)多收
客戶端:
public static void main(String[] args) {
try {
Socket socket = new Socket("localhost",9988);
OutputStream os = socket.getOutputStream();
PrintStream ps = new PrintStream(os);
Scanner scanner = new Scanner(System.in);
while (true){
System.out.println("請輸入:");
String input = scanner.nextLine();
ps.println(input);
ps.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}服務(wù)端:
public static void main(String[] args) {
System.out.println("===服務(wù)端啟動===");
try {
ServerSocket ss = new ServerSocket(9988);
Socket socket = ss.accept();
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String message;
while ((message = br.readLine()) != null){
System.out.println("服務(wù)端接收客戶端信息為:" + message);
}
} catch (IOException e) {
e.printStackTrace();
}
}1.4、BIO 實現(xiàn)客戶端服務(wù)端多對一
服務(wù)端:
public void listen() throws IOException {
ServerSocket serverSocket = null;
try {
log.info("服務(wù)啟動監(jiān)聽");
serverSocket = new ServerSocket(9988);
//循環(huán)接收到客戶端的連接
while (true) {
Socket socket = serverSocket.accept();
//得到連接后,開啟一個線程處理連接
handleSocket(socket);
}
} finally {
if(serverSocket != null){
serverSocket.close();
}
}
}
private void handleSocket(Socket socket) {
HandleSocket socketHandle = new HandleSocket(socket);
new Thread(socketHandle).start();
}public void run() {
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
bufferedInputStream = new BufferedInputStream(socket.getInputStream());
byte[] bytes = new byte[1024];
int len ;
if ((len = bufferedInputStream.read(bytes)) > -1) {
String result = new String(bytes,0,len);
System.out.println("本次接收到的結(jié)果:" + result);
}
System.out.println("回復信息給客戶端:");
bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
String outString = Thread.currentThread().getName() + "接收到了";
bufferedOutputStream.write(outString.getBytes());
bufferedOutputStream.flush();
} catch (IOException e) {
System.out.println("處理異常:" + e.getMessage());
} finally {
try {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
}catch (IOException e){
System.out.println("關(guān)閉流異常:" + e.getMessage());
}
}
}客戶端:
public void start() throws IOException {
Socket socket = new Socket("127.0.0.1", 8081);
String msg = "Hi,This is the BioClient";
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
byte[] bytes = msg.getBytes();
bufferedOutputStream.write(bytes);
bufferedOutputStream.flush();
System.out.println("發(fā)送完畢");
BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
byte[] inBytes = new byte[1024];
int len;
if ((len = bufferedInputStream.read(inBytes)) != -1) {
String result = new String(inBytes, 0, len);
System.out.println("接收到的消息="+result);
}
bufferedOutputStream.close();
bufferedInputStream.close();
socket.close();
}1.5、BIO 模式下的端口轉(zhuǎn)發(fā)思想
一個客戶端的消息經(jīng)由服務(wù)端發(fā)送給所有的客戶端,實現(xiàn)群聊功能。

public class Server {
// 定義一個靜態(tài)集合
public static List<Socket> allSocketOnLine = new ArrayList();
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(9999);
while (true){
Socket socket = ss.accept();
// 把登錄的客戶端socket存入到一個在線的集合中去
allSocketOnLine.add(socket);
// 為當前登錄成功的socket分配一個獨立的線程來處理
new ServerReaderThread(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class ServerReaderThread extends Thread{
private Socket socket;
public ServerReaderThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg;
while ((msg = br.readLine()) != null) {
// 發(fā)送給所有的在線socket
sendMsgToAllClient(msg);
}
} catch (Exception e) {
System.out.println("有人下線了");
Server.allSocketOnLine.remove(socket);
}
}
/**
* 把當前客戶端發(fā)來的消息發(fā)送給全部的在線socket
* @param msg
*/
private void sendMsgToAllClient(String msg) throws IOException {
for (Socket sk : Server.allSocketOnLine) {
PrintWriter pw = new PrintWriter(sk.getOutputStream());
pw.println(msg);
pw.flush();
}
}
}二、NIO 同步非阻塞式 IO(Non-blocking IO)
允許線程在等待IO操作完成期間可以繼續(xù)執(zhí)行其他任務(wù)。
2.1、NIO 3個核心組件(緩沖區(qū)、通道、選擇器)
- 緩沖區(qū)(Buffer):用于存儲數(shù)據(jù)的對象。數(shù)據(jù)從通道讀取到緩沖區(qū),或者從緩沖區(qū)寫入到通道。
- 通道(Channel):既可以從通道中讀取數(shù)據(jù),又可以寫數(shù)據(jù)到通道
- 選擇器(Selector):同時管理多個通道,通過注冊通道的事件(如連接就緒、讀就緒、寫就緒),使用單個線程就能處理多個通道,從而管理多個網(wǎng)絡(luò)連接,提高了效率。

2.2、NIO 主要特性
- 非阻塞I/O:允許線程在等待IO操作完成期間可以繼續(xù)執(zhí)行其他任務(wù)
- IO多路復用:通過選擇器,NIO允許多個通道共用一個線程進行管理,減少了線程的資源消耗。
- 異步IO操作:可以在通道上注冊事件和回調(diào)函數(shù),實現(xiàn)非阻塞的IO操作
- 內(nèi)存映射文件:將文件的一部分或全部直接映射到內(nèi)存中,這樣可以像訪問內(nèi)存一樣訪問文件,提高了文件處理的效率。
- 文件鎖定:允許對文件的部分或全部進行鎖定,從而控制對文件的并發(fā)訪問。
2.3、NIO 與 BIO 的對比
- 面向流與面向緩沖:BIO 是面向流的,每次從流中讀一個或多個字節(jié),直至讀取所有字節(jié);而 NIO 是面向緩沖區(qū)的。
- 阻塞與非阻塞:BIO 的流是阻塞的,當一個線程調(diào)用
read()或write()時,該線程被阻塞,直到有一些數(shù)據(jù)被讀取或數(shù)據(jù)完全寫入;而 NIO 是非阻塞的,一個線程從某通道發(fā)送請求讀取數(shù)據(jù),它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時,就什么都不會獲取。 - 線程開銷:BIO 為每個客戶端連接創(chuàng)建一個線程,在大量并發(fā)連接的情況下會帶來巨大的線程開銷;NIO 通過選擇器實現(xiàn)
I/O多路復用,在一個線程中處理多個通道,減少了線程開銷。
2.4、Buffer 常用子類
ByteBuffer:用于存儲字節(jié)數(shù)據(jù);CharBuffer:用于存儲字符數(shù)據(jù);ShortBuffer:用于存儲Short類型數(shù)據(jù);IntBuffer:用于存儲Int類型數(shù)據(jù);LongBuffer:用于存儲Long類型數(shù)據(jù);FloatBuffer:用于存儲Float類型數(shù)據(jù);DoubleBuffer:用于存儲Double類型數(shù)據(jù);
2.5、Buffer 重要屬性
capacity(容量):表示 Buffer 所占的內(nèi)存大小,不能為負且創(chuàng)建后不能更改limit(限制):表示 Buffer 中可以操作數(shù)據(jù)的大小,不能為負且不能大于 capacity
寫模式下,表示最多能往 Buffer 里寫多少數(shù)據(jù),即 limit 等于 capacity
讀模式下,表示最多能讀到多少數(shù)據(jù),即已寫入的所有數(shù)據(jù)position(位置):表示下一個要讀取或?qū)懭氲臄?shù)據(jù)的索引
緩沖區(qū)位置不能為負,且不能大于其限制
初始 position 值為 0,最大為 capacity – 1。當一個 byte、long 等數(shù)據(jù)寫到 Buffer 后, position 會向前移動到下一個可插入數(shù)據(jù)的 Buffer 單元mark(標記):表示記錄當前 position 的位置,可通過 reset() 恢復到 mark 的位置
三、AIO 異步式 IO(Asynchronous IO)
異步式IO操作不會阻塞線程,而是交由操作系統(tǒng)處理。
完成后,操作系統(tǒng)會通知應(yīng)用程序,或者應(yīng)用程序主動查詢完成狀態(tài)。
使線程在等待IO完成的同時可以執(zhí)行其他任務(wù),提高了系統(tǒng)的并發(fā)性能。
3.1、AIO 核心組件(異步通道、完成處理器)
1.異步通道(Asynchronous Channel):AIO 中進行I/O操作的基礎(chǔ)設(shè)施。
AIO提供了多種異步通道:
AsynchronousSocketChannel(異步套接字通道,支持面向連接的網(wǎng)絡(luò)通信)AsynchronousServerSocketChannel(異步服務(wù)器套接字通道,支持異步服務(wù)器端套接字通信)AsynchronousFileChannel(異步文件通道,支持異步文件讀寫操作)
2.完成處理器(Completion Handler):用于在I/O操作完成后處理結(jié)果的回調(diào)接口。
完成處理器包含兩個方法:
completed(V result, A attachment)在I/O操作成功完成時調(diào)用;failed(Throwable exc, A attachment)在I/O操作失敗時調(diào)用。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
- 淺談Java中BIO、NIO和AIO的區(qū)別和應(yīng)用場景
- Java中網(wǎng)絡(luò)IO的實現(xiàn)方式(BIO、NIO、AIO)介紹
- 詳解Java 網(wǎng)絡(luò)IO編程總結(jié)(BIO、NIO、AIO均含完整實例代碼)
- Java中AIO、BIO、NIO應(yīng)用場景及區(qū)別
- Java中BIO、NIO、AIO的理解
- Java?IO模型之BIO、NIO、AIO三種常見IO模型解析
- java中BIO、NIO、AIO都有啥區(qū)別
- Java網(wǎng)絡(luò)IO模型詳解(BIO、NIO、AIO)
- Java中BIO、NIO和AIO的區(qū)別、原理與用法
- 一文徹底搞懂Java BIO、NIO、AIO的核心區(qū)別
相關(guān)文章
SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印
這篇文章主要為大家詳細介紹了SpringBoot使用iText7實現(xiàn)將HTML轉(zhuǎn)成PDF并添加頁眉頁腳水印的相關(guān)知識,感興趣的小伙伴可以跟隨小編一起學習一下2024-03-03
Java批量向PDF文件中添加圖像水印實現(xiàn)細節(jié)
這篇文章主要為大家介紹了Java批量向PDF文件中添加圖像水印實現(xiàn)細節(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
springboot實現(xiàn)定時任務(wù)@Scheduled方式
這篇文章主要介紹了springboot實現(xiàn)定時任務(wù)@Scheduled方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
Java 響應(yīng)式編程之Spring WebFlux+Reactor 實戰(zhàn)攻略
本文詳細介紹了Java響應(yīng)式編程的核心概念,重點講解了SpringWebFlux框架與Reactor響應(yīng)式庫的實戰(zhàn)用法,通過大量可運行的示例代碼,幫助讀者快速上手并理解響應(yīng)式編程的精髓,本文結(jié)合實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2025-12-12
深度解析JDK和Maven究竟是什么,兩者之間有什么聯(lián)系
JDK是Java開發(fā)基礎(chǔ)工具包,負責編譯、運行,Maven是項目管理工具,處理依賴和構(gòu)建流程,二者協(xié)作支撐項目全生命周期,配置國內(nèi)外鏡像可優(yōu)化依賴下載效率,本文主要給大家講解JDK和Maven究竟是什么,兩者之間有什么聯(lián)系,感興趣的朋友一起看看吧2025-08-08
Java中的HashMap為什么會產(chǎn)生死循環(huán)
這篇文章主要介紹了Java中的HashMap為什么會產(chǎn)生死循環(huán),HashMap?死循環(huán)是一個比較常見、比較經(jīng)典的問題,下面文章我們就來徹底理解死循環(huán)的原因。需要的小伙伴可以參考一下2022-05-05

