日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

深入java核心_Java核心(五)深入理解BIO、NIO、AIO

發(fā)布時間:2025/3/8 java 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 深入java核心_Java核心(五)深入理解BIO、NIO、AIO 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

導讀:本文你將獲取到:同/異步 + 阻/非阻塞的性能區(qū)別;BIO、NIO、AIO 的區(qū)別;理解和實現(xiàn) NIO 操作 Socket 時的多路復用;同時掌握 IO 最底層最核心的操作技巧。

BIO、NIO、AIO 的區(qū)別是什么?

同/異步、阻/非阻塞的區(qū)別是什么?

文件讀寫最優(yōu)雅的實現(xiàn)方式是什么?

NIO 如何實現(xiàn)多路復用功能?

帶著以上這幾個問題,讓我們一起進入IO的世界吧。

在開始之前,我們先來思考一個問題:我們經常所說的“IO”的全稱到底是什么?

可能很多人看到這個問題和我一樣一臉懵逼,IO的全稱其實是:Input/Output的縮寫。

一、IO 介紹

我們通常所說的 BIO 是相對于 NIO 來說的,BIO 也就是 Java 開始之初推出的 IO 操作模塊,BIO 是 BlockingIO 的縮寫,顧名思義就是阻塞 IO 的意思。

1.1 BIO、NIO、AIO的區(qū)別

BIO 就是傳統(tǒng)的 java.io 包,它是基于流模型實現(xiàn)的,交互的方式是同步、阻塞方式,也就是說在讀入輸入流或者輸出流時,在讀寫動作完成之前,線程會一直阻塞在那里,它們之間的調用時可靠的線性順序。它的有點就是代碼比較簡單、直觀;缺點就是 IO 的效率和擴展性很低,容易成為應用性能瓶頸。

NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer 等新的抽象,可以構建多路復用的、同步非阻塞 IO 程序,同時提供了更接近操作系統(tǒng)底層高性能的數(shù)據(jù)操作方式。

AIO 是 Java 1.7 之后引入的包,是 NIO 的升級版本,提供了異步非堵塞的 IO 操作方式,所以人們叫它 AIO(Asynchronous IO),異步 IO 是基于事件和回調機制實現(xiàn)的,也就是應用操作之后會直接返回,不會堵塞在那里,當后臺處理完成,操作系統(tǒng)會通知相應的線程進行后續(xù)的操作。

1.2 全面認識 IO

傳統(tǒng)的 IO 大致可以分為4種類型:

InputStream、OutputStream 基于字節(jié)操作的 IO

Writer、Reader 基于字符操作的 IO

File 基于磁盤操作的 IO

Socket 基于網絡操作的 IO

java.net 下提供的 Scoket 很多時候人們也把它歸為 同步阻塞 IO ,因為網絡通訊同樣是 IO 行為。

java.io 下的類和接口很多,但大體都是 InputStream、OutputStream、Writer、Reader 的子集,所有掌握這4個類和File的使用,是用好 IO 的關鍵。

1.3 IO 使用

接下來看 InputStream、OutputStream、Writer、Reader 的繼承關系圖和使用示例。

1.3.1 InputStream 使用

繼承關系圖和類方法,如下圖:

InputStream 使用示例:

InputStream inputStream = new FileInputStream("D:\\log.txt");

byte[] bytes = new byte[inputStream.available()];

inputStream.read(bytes);

String str = new String(bytes, "utf-8");

System.out.println(str);

inputStream.close();

1.3.2 OutputStream 使用

繼承關系圖和類方法,如下圖:

OutputStream 使用示例:

OutputStream outputStream = new FileOutputStream("D:\\log.txt",true); // 參數(shù)二,表示是否追加,true=追加

outputStream.write("你好,老王".getBytes("utf-8"));

outputStream.close();

1.3.3 Writer 使用

Writer 繼承關系圖和類方法,如下圖:

Writer 使用示例:

Writer writer = new FileWriter("D:\\log.txt",true); // 參數(shù)二,是否追加文件,true=追加

writer.append("老王,你好");

writer.close();

1.3.4 Reader 使用

Reader 繼承關系圖和類方法,如下圖:

Reader 使用示例:

Reader reader = new FileReader(filePath);

BufferedReader bufferedReader = new BufferedReader(reader);

StringBuffer bf = new StringBuffer();

String str;

while ((str = bufferedReader.readLine()) != null) {

bf.append(str + "\n");

}

bufferedReader.close();

reader.close();

System.out.println(bf.toString());

二、同步、異步、阻塞、非阻塞

上面說了很多關于同步、異步、阻塞和非阻塞的概念,接下來就具體聊一下它們4個的含義,以及組合之后形成的性能分析。

2.1 同步與異步

同步就是一個任務的完成需要依賴另外一個任務時,只有等待被依賴的任務完成后,依賴的任務才能算完成,這是一種可靠的任務序列。要么成功都成功,失敗都失敗,兩個任務的狀態(tài)可以保持一致。而異步是不需要等待被依賴的任務完成,只是通知被依賴的任務要完成什么工作,依賴的任務也立即執(zhí)行,只要自己完成了整個任務就算完成了。至于被依賴的任務最終是否真正完成,依賴它的任務無法確定,所以它是不可靠的任務序列。我們可以用打電話和發(fā)短信來很好的比喻同步與異步操作。

2.2 阻塞與非阻塞

阻塞與非阻塞主要是從 CPU 的消耗上來說的,阻塞就是 CPU 停下來等待一個慢的操作完成 CPU 才接著完成其它的事。非阻塞就是在這個慢的操作在執(zhí)行時 CPU 去干其它別的事,等這個慢的操作完成時,CPU 再接著完成后續(xù)的操作。雖然表面上看非阻塞的方式可以明顯的提高 CPU 的利用率,但是也帶了另外一種后果就是系統(tǒng)的線程切換增加。增加的 CPU 使用時間能不能補償系統(tǒng)的切換成本需要好好評估。

2.3 同/異、阻/非堵塞 組合

同/異、阻/非堵塞的組合,有四種類型,如下表:

組合方式性能分析

同步阻塞最常用的一種用法,使用也是最簡單的,但是 I/O 性能一般很差,CPU 大部分在空閑狀態(tài)。

同步非阻塞提升 I/O 性能的常用手段,就是將 I/O 的阻塞改成非阻塞方式,尤其在網絡 I/O 是長連接,同時傳輸數(shù)據(jù)也不是很多的情況下,提升性能非常有效。 這種方式通常能提升 I/O 性能,但是會增加CPU 消耗,要考慮增加的 I/O 性能能不能補償 CPU 的消耗,也就是系統(tǒng)的瓶頸是在 I/O 還是在 CPU 上。

異步阻塞這種方式在分布式數(shù)據(jù)庫中經常用到,例如在網一個分布式數(shù)據(jù)庫中寫一條記錄,通常會有一份是同步阻塞的記錄,而還有兩至三份是備份記錄會寫到其它機器上,這些備份記錄通常都是采用異步阻塞的方式寫 I/O。異步阻塞對網絡 I/O 能夠提升效率,尤其像上面這種同時寫多份相同數(shù)據(jù)的情況。

異步非阻塞這種組合方式用起來比較復雜,只有在一些非常復雜的分布式情況下使用,像集群之間的消息同步機制一般用這種 I/O 組合方式。如 Cassandra 的 Gossip 通信機制就是采用異步非阻塞的方式。它適合同時要傳多份相同的數(shù)據(jù)到集群中不同的機器,同時數(shù)據(jù)的傳輸量雖然不大,但是卻非常頻繁。這種網絡 I/O 用這個方式性能能達到最高。

# 三、優(yōu)雅的文件讀寫

Java 7 之前文件的讀取是這樣的:

// 添加文件

FileWriter fileWriter = new FileWriter(filePath, true);

fileWriter.write(Content);

fileWriter.close();

// 讀取文件

FileReader fileReader = new FileReader(filePath);

BufferedReader bufferedReader = new BufferedReader(fileReader);

StringBuffer bf = new StringBuffer();

String str;

while ((str = bufferedReader.readLine()) != null) {

bf.append(str + "\n");

}

bufferedReader.close();

fileReader.close();

System.out.println(bf.toString());

Java 7 引入了Files(java.nio包下)的,大大簡化了文件的讀寫,如下:

// 寫入文件(追加方式:StandardOpenOption.APPEND)

Files.write(Paths.get(filePath), Content.getBytes(StandardCharsets.UTF_8), StandardOpenOption.APPEND);

// 讀取文件

byte[] data = Files.readAllBytes(Paths.get(filePath));

System.out.println(new String(data, StandardCharsets.UTF_8));

讀寫文件都是一行代碼搞定,沒錯這就是最優(yōu)雅的文件操作。

Files 下還有很多有用的方法,比如創(chuàng)建多層文件夾,寫法上也簡單了:

// 創(chuàng)建多(單)層目錄(如果不存在創(chuàng)建,存在不會報錯)

new File("D://a//b").mkdirs();

四、Socket 和 NIO 的多路復用

本節(jié)帶你實現(xiàn)最基礎的 Socket 的同時,同時會實現(xiàn) NIO 多路復用,還有 AIO 中 Socket 的實現(xiàn)。

4.1 傳統(tǒng)的 Socket 實現(xiàn)

接下來我們將會實現(xiàn)一個簡單的 Socket,服務器端只發(fā)給客戶端信息,再由客戶端打印出來的例子,代碼如下:

int port = 4343; //端口號

// Socket 服務器端(簡單的發(fā)送信息)

Thread sThread = new Thread(new Runnable() {

@Override

public void run() {

try {

ServerSocket serverSocket = new ServerSocket(port);

while (true) {

// 等待連接

Socket socket = serverSocket.accept();

Thread sHandlerThread = new Thread(new Runnable() {

@Override

public void run() {

try (PrintWriter printWriter = new PrintWriter(socket.getOutputStream())) {

printWriter.println("hello world!");

printWriter.flush();

} catch (IOException e) {

e.printStackTrace();

}

}

});

sHandlerThread.start();

}

} catch (IOException e) {

e.printStackTrace();

}

}

});

sThread.start();

// Socket 客戶端(接收信息并打印)

try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) {

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));

bufferedReader.lines().forEach(s -> System.out.println("客戶端:" + s));

} catch (UnknownHostException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

調用 accept 方法,阻塞等待客戶端連接;

利用 Socket 模擬了一個簡單的客戶端,只進行連接、讀取和打印;

在 Java 中,線程的實現(xiàn)是比較重量級的,所以線程的啟動或者銷毀是很消耗服務器的資源的,即使使用線程池來實現(xiàn),使用上述傳統(tǒng)的 Socket 方式,當連接數(shù)極具上升也會帶來性能瓶頸,原因是線程的上線文切換開銷會在高并發(fā)的時候體現(xiàn)的很明顯,并且以上操作方式還是同步阻塞式的編程,性能問題在高并發(fā)的時候就會體現(xiàn)的尤為明顯。

以上的流程,如下圖:

4.2 NIO 多路復用

介于以上高并發(fā)的問題,NIO 的多路復用功能就顯得意義非凡了。

NIO 是利用了單線程輪詢事件的機制,通過高效地定位就緒的 Channel,來決定做什么,僅僅 select 階段是阻塞的,可以有效避免大量客戶端連接時,頻繁線程切換帶來的問題,應用的擴展能力有了非常大的提高。

// NIO 多路復用

ThreadPoolExecutor threadPool = new ThreadPoolExecutor(4, 4,

60L, TimeUnit.SECONDS, new LinkedBlockingQueue());

threadPool.execute(new Runnable() {

@Override

public void run() {

try (Selector selector = Selector.open();

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();) {

serverSocketChannel.bind(new InetSocketAddress(InetAddress.getLocalHost(), port));

serverSocketChannel.configureBlocking(false);

serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {

selector.select(); // 阻塞等待就緒的Channel

Set selectionKeys = selector.selectedKeys();

Iterator iterator = selectionKeys.iterator();

while (iterator.hasNext()) {

SelectionKey key = iterator.next();

try (SocketChannel channel = ((ServerSocketChannel) key.channel()).accept()) {

channel.write(Charset.defaultCharset().encode("你好,世界"));

}

iterator.remove();

}

}

} catch (IOException e) {

e.printStackTrace();

}

}

});

// Socket 客戶端(接收信息并打印)

try (Socket cSocket = new Socket(InetAddress.getLocalHost(), port)) {

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(cSocket.getInputStream()));

bufferedReader.lines().forEach(s -> System.out.println("NIO 客戶端:" + s));

} catch (IOException e) {

e.printStackTrace();

}

首先,通過 Selector.open() 創(chuàng)建一個 Selector,作為類似調度員的角色;

然后,創(chuàng)建一個 ServerSocketChannel,并且向 Selector 注冊,通過指定 SelectionKey.OP_ACCEPT,告訴調度員,它關注的是新的連接請求;

為什么我們要明確配置非阻塞模式呢?這是因為阻塞模式下,注冊操作是不允許的,會拋出 IllegalBlockingModeException 異常;

Selector 阻塞在 select 操作,當有 Channel 發(fā)生接入請求,就會被喚醒;

下面的圖,可以有效的說明 NIO 復用的流程:

就這樣 NIO 的多路復用就大大提升了服務器端響應高并發(fā)的能力。

4.3 AIO 版 Socket 實現(xiàn)

Java 1.7 提供了 AIO 實現(xiàn)的 Socket 是這樣的,如下代碼:

// AIO線程復用版

Thread sThread = new Thread(new Runnable() {

@Override

public void run() {

AsynchronousChannelGroup group = null;

try {

group = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(4));

AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(group).bind(new InetSocketAddress(InetAddress.getLocalHost(), port));

server.accept(null, new CompletionHandler() {

@Override

public void completed(AsynchronousSocketChannel result, AsynchronousServerSocketChannel attachment) {

server.accept(null, this); // 接收下一個請求

try {

Future f = result.write(Charset.defaultCharset().encode("你好,世界"));

f.get();

System.out.println("服務端發(fā)送時間:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));

result.close();

} catch (InterruptedException | ExecutionException | IOException e) {

e.printStackTrace();

}

}

@Override

public void failed(Throwable exc, AsynchronousServerSocketChannel attachment) {

}

});

group.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

} catch (IOException | InterruptedException e) {

e.printStackTrace();

}

}

});

sThread.start();

// Socket 客戶端

AsynchronousSocketChannel client = AsynchronousSocketChannel.open();

Future future = client.connect(new InetSocketAddress(InetAddress.getLocalHost(), port));

future.get();

ByteBuffer buffer = ByteBuffer.allocate(100);

client.read(buffer, null, new CompletionHandler() {

@Override

public void completed(Integer result, Void attachment) {

System.out.println("客戶端打印:" + new String(buffer.array()));

}

@Override

public void failed(Throwable exc, Void attachment) {

exc.printStackTrace();

try {

client.close();

} catch (IOException e) {

e.printStackTrace();

}

}

});

Thread.sleep(10 * 1000);

五、總結

以上基本就是 IO 從 1.0 到目前版本(本文的版本)JDK 8 的核心使用操作了,可以看出來 IO 作為比較常用的基礎功能,發(fā)展變化的改動也很大,而且使用起來也越來越簡單了,IO 的操作也是比較好理解的,一個輸入一個輸出,掌握好了輸入輸出也就掌握好了 IO,Socket 作為網絡交互的集成功能,顯然 NIO 的多路復用,給 Socket 帶來了更多的活力和選擇,用戶可以根據(jù)自己的實際場景選擇相應的代碼策略。

六、參考文檔

總結

以上是生活随笔為你收集整理的深入java核心_Java核心(五)深入理解BIO、NIO、AIO的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产三级中文字幕 | 久久久久夜 | 99久久精品国产成人一区二区 | 国产三级做爰高清在线 | av第一福利大全导航 | 中国女人一级一次看片 | 国产精品高潮呻吟久久 | 日本一区二区三区久久 | 亚洲色图导航 | 日韩免费视频一区 | 精品久久国产字幕高潮 | 一区二区三区四区高清视频 | 美国式禁忌1980 | 狠狠澡| 日韩精品人妻中文字幕有码 | 熟妇人妻无码xxx视频 | 99综合网| 欧美日一本 | 人成亚洲| 亚洲三级在线 | 全肉的吸乳文 | 美女流白浆视频 | 别揉我奶头啊嗯一区二区 | 少妇一级视频 | 亚州av| 成人免费影视网站 | 亚洲一区二区三区观看 | av大片在线免费观看 | 天天操,夜夜操 | www夜插内射视频网站 | 成人三级影院 | 黑人玩弄人妻一区二区三区 | 免费黄片毛片 | 韩国精品一区二区三区 | 中文字字幕一区二区三区四区五区 | 亚洲国产97在线精品一区 | 欧美日韩中文字幕在线 | 欧美 亚洲 视频 | 免费视频a | 中出视频在线观看 | 一区二区三区少妇 | 波多一区二区 | 日日射天天射 | 这里有精品视频 | 影音先锋欧美在线 | 人人爽在线 | 久久久久成人精品免费播放动漫 | 欧美一区二区三区久久久 | 国产精品视屏 | 久久久久久爱 | 99久久这里只有精品 | 日韩国产一区 | 日本在线视频播放 | 亚洲图片88 | 一个色综合久久 | 黄色高清无遮挡 | 亚洲av久久久噜噜噜熟女软件 | 国产一级视频免费观看 | 人妻中文字幕一区 | 欧美xxxx日本和非洲 | 亚洲专区在线播放 | 亚洲欧洲自拍偷拍 | 欧美 丝袜 自拍 制服 另类 | 动漫一区二区三区 | 99久久精品一区二区 | 超碰97在线资源 | 亚洲制服一区二区 | 性欧美欧美巨大69 | 一级做a爱视频 | 亚洲欧美成人一区二区三区 | a级片在线免费观看 | 久久久久久久久久一区二区三区 | 国产最爽的乱淫视频国语对白 | 亚洲一区二区三区成人 | 国产免费久久精品国产传媒 | 欧美日日| 黄色的毛片 | 韩国午夜影院 | 久久婷婷六月 | 美女久久 | 日韩欧美在线第一页 | 黄色美女av | 色狠狠久久av大岛优香 | 国产黄色网页 | 久久一精品 | 爱情岛亚洲首页论坛 | 久草免费在线视频 | 日韩欧美中文字幕在线观看 | 91网站免费看 | 欧美日韩黄 | 665566综合网 | 国产在线观看免费视频今夜 | 免费av电影网址 | 天海翼视频在线观看 | 波波野结衣 | www.插插| 国产欧美精品在线观看 | 美女毛片 | 欧色丰满女同hd |