Java中BIO、NIO和AIO舉例總結(jié)
前言
在講 BIO,NIO,AIO 之前先來回顧一下這樣幾個(gè)概念:同步與異步,阻塞與非阻塞。
1.同步與異步
同步調(diào)用(Synchronous Cal): 同步就是發(fā)起一個(gè)調(diào)用后,被調(diào)用者未處理完請(qǐng)求之前,調(diào)用不返回
代碼示例:
int result = calculateSum(3, 5); // 等到函數(shù)調(diào)用完成后,獲取返回結(jié)果
System.out.println(result); // 輸出計(jì)算結(jié)果 8
public static int calculateSum(int a, int b) {
// 同步調(diào)用:當(dāng)前線程會(huì)阻塞,直到函數(shù)計(jì)算完成并返回結(jié)果
return a + b;
}
異步調(diào)用(Asynchronous Call): 異步就是發(fā)起一個(gè)調(diào)用后,立刻得到被調(diào)用者的回應(yīng)表示已接收到請(qǐng)求,但是被調(diào)用者并沒有返回結(jié)果,此時(shí)我們可以處理其他的請(qǐng)求,被調(diào)用者通常依靠事件,回調(diào)等機(jī)制來通知調(diào)用者其返回結(jié)果。
代碼示例(使用回調(diào)): 調(diào)用者傳遞一個(gè)函數(shù)給被調(diào)用者,被調(diào)用者在完成后調(diào)用該函數(shù)。
interface Callback {
void onComplete(int result);
}
void calculateSumAsync(int a, int b, Callback callback) {
new Thread(() -> {
int result = a + b;
callback.onComplete(result); // 完成后通知調(diào)用者
}).start();
}
// 調(diào)用
calculateSumAsync(3, 5, (result) -> System.out.println("結(jié)果: " + result));代碼示例(使用事件):被調(diào)用者通過觸發(fā)事件(如按鈕點(diǎn)擊、網(wǎng)絡(luò)請(qǐng)求完成)通知調(diào)用者。
class EventEmitter {
private List<EventListener> listeners = new ArrayList<>();
void addListener(EventListener listener) {
listeners.add(listener);
}
void doWork() {
new Thread(() -> {
int result = 42;
for (EventListener listener : listeners) {
listener.onEvent(result); // 觸發(fā)事件
}
}).start();
}
}
interface EventListener {
void onEvent(int data);
}
// 調(diào)用
EventEmitter emitter = new EventEmitter();
emitter.addListener(data -> System.out.println("收到事件: " + data));
emitter.doWork();同步調(diào)用中調(diào)用者發(fā)出請(qǐng)求后,必須阻塞等待被調(diào)用者完成處理并返回結(jié)果,異步調(diào)用中調(diào)用者發(fā)出請(qǐng)求后立即獲得"已接收"響應(yīng),但此時(shí)可能尚未得到最終處理結(jié)果
2.阻塞和非阻塞
阻塞: 阻塞就是發(fā)起一個(gè)請(qǐng)求,調(diào)用者一直等待請(qǐng)求結(jié)果返回,也就是當(dāng)前線程會(huì)被掛起,無法從事其他任務(wù),只有當(dāng)條件就緒才能繼續(xù)。
非阻塞: 非阻塞就是發(fā)起一個(gè)請(qǐng)求,調(diào)用者不用一直等著結(jié)果返回,可以先去干其他事情。
阻塞是指線程在等待某個(gè)條件或資源時(shí)被掛起,不能繼續(xù)執(zhí)行;而非阻塞是指線程不會(huì)被掛起,操作立即返回,能快速判斷是否成功繼續(xù)執(zhí)行。阻塞常用于簡單同步場(chǎng)景,但性能較低;非阻塞適合高并發(fā)環(huán)境,效率更高。
同/異步與阻塞/非阻塞區(qū)別:
同步與異步主要關(guān)注的是調(diào)用結(jié)果的獲取方式:同步調(diào)用中,調(diào)用方在發(fā)起請(qǐng)求后需要等待操作完成并直接獲得返回結(jié)果;而異步調(diào)用中,調(diào)用方發(fā)起請(qǐng)求后無需立刻獲取結(jié)果,結(jié)果會(huì)在任務(wù)完成后通過回調(diào)、Future 或事件通知等方式返回。
阻塞與非阻塞主要關(guān)注的是等待期間線程的執(zhí)行狀態(tài):阻塞表示線程在等待結(jié)果時(shí)被掛起,無法執(zhí)行其他任務(wù);非阻塞表示線程在等待期間不會(huì)被掛起,仍可以繼續(xù)執(zhí)行其他邏輯。
3.BIO,NIO,AIO
3.1 BIO (Blocking I/O)
BIO 是 Java 早期最經(jīng)典的 I/O 模型,核心特征是 同步 + 阻塞:線程發(fā)起讀/寫后,會(huì)一直阻塞等待數(shù)據(jù)就緒以及拷貝完成,期間不能做其他事情。在服務(wù)端模型中,常見寫法是 一個(gè)連接對(duì)應(yīng)一個(gè)線程(或線程池),實(shí)現(xiàn)簡單但并發(fā)高時(shí)線程開銷大、容易出現(xiàn)上下文切換和資源耗盡問題。
// BIO 服務(wù)端:阻塞式接收 + 阻塞式讀取(一個(gè)連接一個(gè)線程示例)
import java.io.*;
import java.net.*;
public class BioServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(8080);
System.out.println("BIO server start...");
while (true) {
Socket socket = server.accept(); // 阻塞:等待連接
new Thread(() -> {
try (BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {
String line = in.readLine(); // 阻塞:等待數(shù)據(jù)
out.println("BIO reply: " + line);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
適用場(chǎng)景:低并發(fā)、連接數(shù)不多、開發(fā)快速驗(yàn)證;或業(yè)務(wù)處理很重、連接少(線程數(shù)可控)。
3.2 NIO (No-Blocking I/O)
NIO 是 Java 1.4 引入的 I/O 框架,對(duì)應(yīng) java.nio 包。NIO 的核心是 同步 + 非阻塞:讀寫調(diào)用可以立即返回(可能讀到 0 字節(jié)),應(yīng)用通過 Selector 多路復(fù)用在一個(gè)線程內(nèi)管理多個(gè)連接,減少線程數(shù)量與上下文切換成本。NIO 采用 Buffer(緩沖區(qū))+ Channel(通道)的方式進(jìn)行數(shù)據(jù)讀寫,并支持阻塞/非阻塞兩種模式,但在高并發(fā)網(wǎng)絡(luò)編程中通常使用 非阻塞 + Selector。
// NIO 服務(wù)端:Selector 多路復(fù)用(單線程處理多個(gè)連接)
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
public class NioServer {
public static void main(String[] args) throws Exception {
ServerSocketChannel server = ServerSocketChannel.open();
server.bind(new InetSocketAddress(8081));
server.configureBlocking(false);
Selector selector = Selector.open();
server.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NIO server start...");
ByteBuffer buf = ByteBuffer.allocate(1024);
while (true) {
selector.select(); // 阻塞等待“就緒事件”(可認(rèn)為等待通知)
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = it.next();
it.remove();
if (key.isAcceptable()) {
SocketChannel sc = server.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
SocketChannel sc = (SocketChannel) key.channel();
buf.clear();
int n = sc.read(buf); // 非阻塞讀:可能返回 0 或 -1
if (n <= 0) {
sc.close();
continue;
}
buf.flip();
sc.write(ByteBuffer.wrap(("NIO reply: ").getBytes()));
sc.write(buf);
}
}
}
}
}
適用場(chǎng)景:高并發(fā)連接、多連接 I/O 頻繁(網(wǎng)絡(luò)服務(wù)器、網(wǎng)關(guān)、聊天室等),希望用少量線程處理大量連接。
3.3 AIO (Asynchronous I/O)
AIO(也常稱 NIO.2)在 Java 7 引入,對(duì)應(yīng) java.nio.channels.Asynchronous 系列。AIO 的核心特征是 異步 + 非阻塞:應(yīng)用發(fā)起 I/O 操作后立即返回,不需要自己輪詢或等待,操作完成后由系統(tǒng)通過回調(diào)(CompletionHandler)或 Future 通知結(jié)果。相比 NIO 的“同步非阻塞(事件就緒后自己讀寫)”,AIO 更接近“真正的異步”:你只需要提交任務(wù),完成后得到通知。
// AIO 服務(wù)端:異步 accept + 異步 read(回調(diào)驅(qū)動(dòng))
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.CountDownLatch;
public class AioServer {
public static void main(String[] args) throws Exception {
AsynchronousServerSocketChannel server =
AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8082));
System.out.println("AIO server start...");
CountDownLatch latch = new CountDownLatch(1);
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
@Override
public void completed(AsynchronousSocketChannel ch, Void att) {
// 繼續(xù)接收下一個(gè)連接(非常關(guān)鍵,否則只接一次)
server.accept(null, this);
ByteBuffer buf = ByteBuffer.allocate(1024);
ch.read(buf, buf, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer n, ByteBuffer b) {
try {
if (n <= 0) { ch.close(); return; }
b.flip();
ch.write(ByteBuffer.wrap("AIO reply: ".getBytes())).get();
ch.write(b).get();
ch.close();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, ByteBuffer b) {
try { ch.close(); } catch (Exception ignored) {}
}
});
}
@Override
public void failed(Throwable exc, Void att) {
exc.printStackTrace();
latch.countDown();
}
});
latch.await(); // 防止主線程退出
}
}
BIO、AIO、NIO小結(jié):
BIO:同步阻塞,簡單但并發(fā)高會(huì)吃線程
NIO:同步非阻塞,Selector 管理多連接,適合高并發(fā)
AIO:異步非阻塞,回調(diào)/通知獲取結(jié)果,編程模型更異步
總結(jié)
到此這篇關(guān)于Java中BIO、NIO和AIO舉例總結(jié)的文章就介紹到這了,更多相關(guān)Java中BIO、NIO和AIO內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 淺談Java中BIO、NIO和AIO的區(qū)別和應(yīng)用場(chǎng)景
- Java中網(wǎng)絡(luò)IO的實(shí)現(xiàn)方式(BIO、NIO、AIO)介紹
- 詳解Java 網(wǎng)絡(luò)IO編程總結(jié)(BIO、NIO、AIO均含完整實(shí)例代碼)
- Java中AIO、BIO、NIO應(yīng)用場(chǎng)景及區(qū)別
- Java中BIO、NIO、AIO的理解
- java的三種IO模型詳解(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ū)別、原理與用法
相關(guān)文章
java easyUI實(shí)現(xiàn)自定義網(wǎng)格視圖實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于java easyUI實(shí)現(xiàn)自定義網(wǎng)格視圖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-10-10
Java并發(fā)編程之ReentrantLock實(shí)現(xiàn)原理及源碼剖析
ReentrantLock 是常用的鎖,相對(duì)于Synchronized ,lock鎖更人性化,閱讀性更強(qiáng),文中將會(huì)詳細(xì)的說明,請(qǐng)君往下閱讀2021-09-09
Java多線程提交按照時(shí)間順序獲取線程結(jié)果詳解流程
在工作中是否存在這樣的場(chǎng)景,多個(gè)線程提交執(zhí)行,你不想全部線程執(zhí)行結(jié)束了獲取結(jié)果,而是有線程完成返回結(jié)果就獲取消費(fèi)。本文提供該場(chǎng)景的工具類,可以直接用哦2021-11-11
Springboot接收?Form?表單數(shù)據(jù)的示例詳解
這篇文章主要介紹了Springboot接收?Form?表單數(shù)據(jù)的實(shí)例代碼,本文通過圖文實(shí)例代碼相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08

