- I/O 模型簡單的理解:就是用什么樣的通道進行數(shù)據(jù)的發(fā)送和接收叫惊,很大程度上決定了程序通信的性能
- Java 共支持 3 種網(wǎng)絡編程模型/IO 模式:BIO款青、NIO、AIO
-
Java BIO : 同步并阻塞(傳統(tǒng)阻塞型)霍狰,服務器實現(xiàn)模式為一個連接一個線程抡草,即客戶端有連接請求時服務器 端就需要啟動一個線程進行處理饰及,如果這個連接不做任何事情會造成不必要的線程開銷、BIO 方式適用于連接數(shù)目比較小且固定的架構康震,這種方式對服務器資源要求比較高燎含,并發(fā)局限于應用中,JDK1.4以前的唯一選擇腿短,但程序簡單易理解屏箍。
-
同步非阻塞,服務器實現(xiàn)模式為一個線程處理多個請求(連接)橘忱,即客戶端發(fā)送的連接請求都會注 冊到多路復用器上赴魁,多路復用器輪詢到連接有 I/O 請求就進行處理,NIO 方式適用于連接數(shù)目多且連接比較短(輕操作)的架構钝诚,比如聊天服務器颖御,彈幕系統(tǒng),服務器間通訊等凝颇。編程比較復雜潘拱,JDK1.4 開始支持。
- Java AIO(NIO.2) : 異步非阻塞拧略,AIO 引入異步通道的概念芦岂,采用了 Proactor 模式,簡化了程序編寫垫蛆,有效 的請求才啟動線程禽最,它的特點是先由操作系統(tǒng)完成后才通知服務端程序啟動線程去處理,一般適用于連接數(shù)較 多且連接時間較長的應用月褥。AIO 方式使用于連接數(shù)目多且連接比較長(重操作)的架構弛随,比如相冊服務器瓢喉,充分調用 OS 參與并發(fā)操作宁赤,編程比較復雜,JDK7 開始支持栓票。
-
Java BIO : 同步并阻塞(傳統(tǒng)阻塞型)霍狰,服務器實現(xiàn)模式為一個連接一個線程抡草,即客戶端有連接請求時服務器 端就需要啟動一個線程進行處理饰及,如果這個連接不做任何事情會造成不必要的線程開銷、BIO 方式適用于連接數(shù)目比較小且固定的架構康震,這種方式對服務器資源要求比較高燎含,并發(fā)局限于應用中,JDK1.4以前的唯一選擇腿短,但程序簡單易理解屏箍。
Java BIO 問題分析
- 每個請求都需要創(chuàng)建獨立的線程决左,與對應的客戶端進行數(shù)據(jù) Read,業(yè)務處理走贪,數(shù)據(jù) Write 佛猛。
- 當并發(fā)數(shù)較大時,需要創(chuàng)建大量線程來處理連接坠狡,系統(tǒng)資源占用較大继找。
- 連接建立后,如果當前線程暫時沒有數(shù)據(jù)可讀逃沿,則線程就阻塞在 Read 操作上婴渡,造成線程資源浪費
nio介紹
- Java NIO 全稱 java non-blocking IO幻锁,是指 JDK 提供的新 API。從 JDK1.4 開始边臼,Java 提供了一系列改進的輸入/輸出的新特性哄尔,被統(tǒng)稱為 NIO(即 New IO),是同步非阻塞的
- NIO 相關類都被放在 java.nio 包及子包下柠并,并且對原 java.io 包中的很多類進行改寫岭接。【基本案例】
- NIO 有三大核心部分:Channel(通道)臼予,Buffer(緩沖區(qū)), Selector(選擇器)
Selector 鸣戴、 Channel 和 Buffer 的關系圖
- 每個channel都會對應一個Buffer
- Selector 對應一個線程, 一個線程對應多個 channel(連接)
- 該圖反應了有三個channel注冊到該selector//程序
- 程序切換到哪個channel是有事件決定的,Event就是一個重要的概念
- Selector 會根據(jù)不同的事件瘟栖,在各個通道上切換
- Buffer 就是一個內存塊 葵擎, 底層是有一個數(shù)組
- 數(shù)據(jù)的讀取寫入是通過Buffer,這個和BIO,BIO中要么是輸入流,或者是輸出流, 不能雙向半哟,但是 NIO 的 Buffer 是可以讀也可以寫, 需要 flip 方法切換 channel 是雙向的, 可以返回底層操作系統(tǒng)的情況, 比如 Linux 酬滤, 底層的操作系統(tǒng)通道就是雙向的。
緩沖區(qū)(Buffer)
緩沖區(qū)(Buffer):緩沖區(qū)本質上是一個可以讀寫數(shù)據(jù)的內存塊寓涨,可以理解成是一個容器對象(含數(shù)組)盯串,該對象提供了一組方法,可以更輕松地使用內存塊戒良,体捏,緩沖區(qū)對象內置了一些機制,能夠跟蹤和記錄緩沖區(qū)的狀態(tài)變化情況糯崎。Channel 提供從文件几缭、網(wǎng)絡讀取數(shù)據(jù)的渠道,但是讀取或寫入的數(shù)據(jù)都必須經由 Buffer沃呢。
channel
NIO 的通道類似于流年栓,但有些區(qū)別如下:
- 通道可以同時進行讀寫,而流只能讀或者只能寫
- 通道可以實現(xiàn)異步讀寫數(shù)據(jù)
- 通道可以從緩沖讀數(shù)據(jù)薄霜,也可以寫數(shù)據(jù)到緩沖:
- BIO 中的 stream 是單向的某抓,例如 FileInputStream 對象只能進行讀取數(shù)據(jù)的操作,而 NIO 中的通道(Channel) 是雙向的惰瓜,可以讀操作否副,也可以寫操作。
- Channel 在 NIO 中是一個接口
public interface Channel extends Closeable{}
- 常 用 的 Channel 類 有 : FileChannel 崎坊、 DatagramChannel 备禀、 ServerSocketChannel 和 SocketChannel 。 【ServerSocketChanne 類似 ServerSocket , SocketChannel 類似 Socket】
Selector 示意圖和特點說明
- Java 的 NIO,用非阻塞的 IO 方式曲尸∩氪可以用一個線程,處理多個的客戶端連接队腐,就會使用到 Selector(選擇器)
- Selector 能夠檢測多個注冊的通道上是否有事件發(fā)生(注意:多個 Channel 以事件的方式可以注冊到同一個 Selector)蚕捉,如果有事件發(fā)生,便獲取事件然后針對每個事件進行相應的處理柴淘。這樣就可以只用一個單線程去管 理多個通道迫淹,也就是管理多個連接和請求。
- 只有在連接/通道真正有讀寫事件發(fā)生時为严,才會進行讀寫敛熬,就大大地減少了系統(tǒng)開銷,并且不必為每個連接都 創(chuàng)建一個線程第股,不用去維護多個線程
- 避免了多線程之間的上下文切換導致的開銷
-
Netty 的 IO 線程 NioEventLoop 聚合了 Selector(選擇器应民,也叫多路復用器),可以同時并發(fā)處理成百上千個客
戶端連接夕吻。
當線程從某客戶端 Socket 通道進行讀寫數(shù)據(jù)時诲锹,若沒有數(shù)據(jù)可用時,該線程可以進行其他任務涉馅。
-
線程通常將非阻塞 IO 的空閑時間用于在其他通道上執(zhí)行 IO 操作归园,所以單獨的線程可以管理多個輸入和輸出
通道。
-
由于讀寫操作都是非阻塞的稚矿,這就可以充分提升 IO 線程的運行效率庸诱,避免由于頻繁 I/O 阻塞導致的線程掛
起。
一個 I/O 線程可以并發(fā)處理 N 個客戶端連接和讀寫操作晤揣,這從根本上解決了傳統(tǒng)同步阻塞 I/O 一連接一線程模型桥爽,架構的性能、彈性伸縮能力和可靠性都得到了極大的提升昧识。