一文了解Java的I/O演進(jìn)之路
1. I/O 模型基本說明
I/O 模型:就是用什么樣的通道或者說是通信模式和架構(gòu)進(jìn)行數(shù)據(jù)的傳輸和接收,很大程度上決定了程序通信的性能。 Java 共支持 3 種網(wǎng)絡(luò)編程的/IO 模型:
- BIO
- NIO
- AIO
實(shí)際通信需求下,要根據(jù)不同的業(yè)務(wù)場(chǎng)景和性能需求決定選擇不同的I/O模型
2. Java BIO
- Java BIO 就是傳統(tǒng)的java io 編程,其相關(guān)的類和接口在 java.io
- BIO(blocking I/O) : 同步阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開銷,可以通過線程池機(jī)制改善(實(shí)現(xiàn)多個(gè)客戶連接服務(wù)器)
圖片
Java BIO 工作機(jī)制
BIO 編程流程的梳理
服務(wù)器端啟動(dòng)一個(gè) ServerSocket,注冊(cè)端口,調(diào)用accpet方法監(jiān)聽客戶端的Socket連接。
- 客戶端啟動(dòng) Socket 對(duì)服務(wù)器進(jìn)行通信,默認(rèn)情況下服務(wù)器端需要對(duì)每個(gè)客戶 建立一個(gè)線程與之通訊
3. Java NIO
- Java NIO(New IO)也有人稱之為 java non-blocking IO是從Java 1.4版本開始引入的一個(gè)新的IO API,可以替代標(biāo)準(zhǔn)的Java IO API。NIO與原來的IO有同樣的作用和目的,但是使用的方式完全不同,NIO支持面向緩沖區(qū)的、基于通道的IO操作。NIO將以更加高效的方式進(jìn)行文件的讀寫操作。NIO可以理解為非阻塞IO,傳統(tǒng)的IO的read和write只能阻塞執(zhí)行,線程在讀寫IO期間不能干其他事情,比如調(diào)用socket.read()時(shí),如果服務(wù)器一直沒有數(shù)據(jù)傳輸過來,線程就一直阻塞,而NIO中可以配置socket為非阻塞模式。
- NIO 相關(guān)類都被放在 java.nio 包及子包下,并且對(duì)原 java.io 包中的很多類進(jìn)行改寫。
- NIO 有三大核心部分:Channel( 通道) ,Buffer( 緩沖區(qū)), Selector( 選擇器)
- Java NIO 的非阻塞模式,使一個(gè)線程從某通道發(fā)送請(qǐng)求或者讀取數(shù)據(jù),但是它僅能得到目前可用的數(shù)據(jù),如果目前沒有數(shù)據(jù)可用時(shí),就什么都不會(huì)獲取,而不是保持線程阻塞,所以直至數(shù)據(jù)變的可以讀取之前,該線程可以繼續(xù)做其他的事情*。 非阻塞寫也是如此,一個(gè)線程請(qǐng)求寫入一些數(shù)據(jù)到某通道,但不需要等待它完全寫入,這個(gè)線程同時(shí)可以去做別的事情。
- 通俗理解:NIO 是可以做到用一個(gè)線程來處理多個(gè)操作的。假設(shè)有 1000 個(gè)請(qǐng)求過來,根據(jù)實(shí)際情況,可以分配20 或者 80個(gè)線程來處理。不像之前的阻塞 IO 那樣,非得分配 1000 個(gè)
圖片
NIO 三大核心原理示意圖
Buffer緩沖區(qū)
緩沖區(qū)本質(zhì)上是一塊可以寫入數(shù)據(jù),然后可以從中讀取數(shù)據(jù)的內(nèi)存。這塊內(nèi)存被包裝成NIO Buffer對(duì)象,并提供了一組方法,用來方便的訪問該塊內(nèi)存。相比較直接對(duì)數(shù)組的操作,Buffer API更加容易操作和管理。
Channel(通道)
Java NIO的通道類似流,但又有些不同:既可以從通道中讀取數(shù)據(jù),又可以寫數(shù)據(jù)到通道。但流的(input或output)讀寫通常是單向的。 通道可以非阻塞讀取和寫入通道,通道可以支持讀取或?qū)懭刖彌_區(qū),也支持異步地讀寫。
Selector選擇器
Selector是 一個(gè)Java NIO組件,可以能夠檢查一個(gè)或多個(gè) NIO 通道,并確定哪些通道已經(jīng)準(zhǔn)備好進(jìn)行讀取或?qū)懭?。這樣,一個(gè)單獨(dú)的線程可以管理多個(gè)channel,從而管理多個(gè)網(wǎng)絡(luò)連接,提高效率
圖片
- 每個(gè) channel 都會(huì)對(duì)應(yīng)一個(gè) Buffer
- 一個(gè)線程對(duì)應(yīng)Selector , 一個(gè)Selector對(duì)應(yīng)多個(gè) channel(連接)
- 程序切換到哪個(gè) channel 是由事件決定
- Selector 會(huì)根據(jù)不同的事件,在各個(gè)通道上切換
- Buffer 就是一個(gè)內(nèi)存塊 , 底層是一個(gè)數(shù)組
- 數(shù)據(jù)的讀取寫入是通過 Buffer完成的 , BIO 中要么是輸入流,或者是輸出流, 不能雙向,但是 NIO 的 Buffer 是可以讀也可以寫。
- Java NIO系統(tǒng)的核心在于通道(Channel)和緩沖區(qū) (Buffer)。通道表示打開到 IO 設(shè)備(例如:文件、 套接字)的連接。若需要使用 NIO 系統(tǒng),需要獲取 用于連接 IO 設(shè)備的通道以及用于容納數(shù)據(jù)的緩沖 區(qū)。然后操作緩沖區(qū),對(duì)數(shù)據(jù)進(jìn)行處理。簡(jiǎn)而言之,Channel 負(fù)責(zé)傳輸, Buffer 負(fù)責(zé)存取數(shù)據(jù)
Java AIO
Java AIO(NIO.2): 異步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由OS先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線程進(jìn)行處理,一般適用于連接數(shù)較多且連接時(shí)間較長(zhǎng)的應(yīng)用
與NIO不同,當(dāng)進(jìn)行讀寫操作時(shí),只須直接調(diào)用API的read或write方法即可, 這兩種方法均為異步的,對(duì)于讀操作而言,當(dāng)有流可讀取時(shí),操作系統(tǒng)會(huì)將可讀的流傳入read方法的緩沖區(qū),對(duì)于寫操作而言,當(dāng)操作系統(tǒng)將write方法傳遞的流寫入完畢時(shí),操作系統(tǒng)主動(dòng)通知應(yīng)用程序
BIO、NIO、AIO 適用場(chǎng)景分析
- BIO 方式適用于連接數(shù)目比較小且固定的架構(gòu),這種方式對(duì)服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中,JDK1.4以前的唯一選擇,但程序簡(jiǎn)單易理解。
- NIO 方式適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天服務(wù)器,彈幕系統(tǒng),服務(wù)器間通訊等。 編程比較復(fù)雜,JDK1.4 開始支持。
- AIO 方式使用于連接數(shù)目多且連接比較長(zhǎng)(重操作)的架構(gòu),比如相冊(cè)服務(wù)器,充分調(diào)用 OS 參與并發(fā)操作, 編程比較復(fù)雜,JDK7 開始支持。