第一次听人用男女关系讲 N(Non-Blocking)I(进)O(出),涨姿势了
BIO:80 年代屌絲追妹
80 年代屌絲男買了一個 BP 機用來追妹,男士使用傳呼臺給女生留言:
男:下午一起看個電影?[早晨 10 點]
這是男生唯一心動的女生,所以一直守著自己的 BP 機,等待女生回復,就這樣一天過去了,直到:
男:BP 沒電,自動關機。
名詞解釋
BP 機和傳呼臺指的是 BIO 中的流單向傳輸的特性,屌絲男士通過傳呼臺給 BP 機發送消息是單向的,如果是女生通過傳呼臺回復屌絲男士,也是單向的。
這個女生是男士唯一心動的女生,所以他傻等著 BP 機回復,即便可能一直不會有消息,這就是同步阻塞 IO,中 B 的概念。
BIO 的缺點
同步阻塞 IO,如果存在多個請求的時候,服務端必須通過多線程處理,增加了服務端的壓力和創建銷毀線程的開銷。
如果連接一直沒有響應,服務端也需要一直監聽端口等待,浪費了服務端資源。
NIO:80 年代公子哥把妹
80 年代的公子哥買了一個大哥大,關鍵是這個公子哥太花心,同時中意了兩個妹子,于是他就開始了把妹過程。
公子哥用大哥大給女 A 打電話:
公子哥 -> 女 A:下午一起看個電影?[早晨 10 點]
女 A -> 公子哥:我正在吃飯,你晚點再打過來?
公子哥用大哥大給女 B 打電話:
公子哥 -> 女 B:下午一起看個電影?[早晨 10 點 1 分]
女 B -> 公子哥:我正在吃飯,你晚點再打過來?
過了 10 分鐘公子哥再次打電話詢問
公子哥 -> 女 A:怎么樣,有空嗎?[早晨 10 點 10 分]
女 A -> 公子哥:我下午陪爸爸打馬球,不去了。
公子哥 -> 女 B:怎么樣,有空嗎?[早晨 10 點 11 分]
女 B -> 公子哥:好呀,下午 3 點來我家接我吧。
公子哥 -> 女 B:好嘞,我開車去接你。
最終公子哥成功了追求到了女 B,這個故事告訴我們,成功的前提是有錢。(你怎么看?)
名詞解釋
公子哥用上了大哥大,可以實現雙向的通話,這就是 NIO 中的 Channel,可以實現雙向的數據流傳輸。
公子哥不用像 BP 機小哥一樣死等著回復,每次打電話都能得到回復,掛斷電話一會兒再來詢問即可,這就是 Channel 的非阻塞特性,也就是 “N” 的體現。
公子哥可以同時撩兩個妹子,這就是 NIO 的 IO 多路復用,也就是 Selector。
公子哥只能同時和一個人通話,這就是同步,所以 NIO 的全稱叫做同步非阻塞 IO。
優缺點
非阻塞 IO 模型,不需要阻塞在特定的請求。
一個線程可以處理多個請求,不需要客戶端和服務器端一比一的對應,沒有多線程創建和銷毀帶來的系統開銷。
服務端不需要死等請求,減少了服務端壓力。
關鍵名詞
Channel,雙向傳輸,非阻塞的通道,有FileChannel,DatagramChannel,ServerSocketChannel/SocketChannel 等。
Buffer,數據塊的讀寫,可以理解字節數組,效率高。四個重要屬性:capacity 容量,position 位置, limit 上限,用戶切換讀寫時候的游標,mark 標記,標記 position 的位置,分為堆內內存和堆外內存,也是 NIO 性能的關鍵內容。
Selector,IO 多路復用的關鍵,實現了循環查看可以使用的 Channel,解決死等問題。
Selector 實現有多種方式,自己寫一個數組循環也是方式,也可以實現 IO 多路復用,只是性能好壞而已,所以基于底層 poll、select和epoll 也是實現“遍歷”可用通道的方式不同而已。select 使用輪詢的方式,有 1024 個連接的限制,poll 去掉了這個限制依然是輪詢的方式,epoll 是基于系統的注冊回調的方式,監聽系統的事件實現。
NIO 引入了 Buffer 的概念,每次使用 Buffer 拷貝數據其實是一次從用戶空間(JVM) 向系統空間(系統內存) 的一次拷貝, Java 里面提供了 DirectByteBuffer 堆外內存,如果使用使用堆外內存,可以減少一次系統空間和用戶空間的拷貝,這種現象叫做零拷貝。強調一下,并不是操作系統不能直接操作 HeapByteBuffer(對內內存),而且在 GC 的作用下,內存地址可能隨時變化,操作的內存數據不一定準備。
IO 多路復用性能更好,針對的 I/O 密集型應用程序,如果是 CPU 密集型應用程序,還是通過多線程的方案。所以很多寫 IO 多路復用的文章都會說“多線程的創建,必然存在創建銷毀和切換的開銷,在高并發系統中,會拖慢整個系統”,其實并不是非常的準確,雖然是想說明 I/O 多路復用的利好,但是確實有點以偏概全。
AIO:21 世紀非智能時代大學生把妹
21 世紀初期,還沒有智能機,不過諾基亞 1110 砸核桃神機已經普及了,下面就是新時代大學生小王用自己的諾基亞 1110 的把妹過程。
小王給中意的兩個女生直接發短信留言(群發):
小王 -> 女 A:下午一起看個電影?[早晨 10 點]
小王 -> 女 B:下午一起看個電影?[早晨 10 點]
發完短信小王去看《西游記》去了。10分鐘以后電話響起,收到了妹子的短信,小王拿起了手機閱讀了消息并進行回復。
女 B -> 小王:好呀,下午 3 點來我家接我吧 [早晨 10 點 10 分]
小王 -> 女 B:好的,我去接你不見不散。
名詞解釋
小王發完短信不需要盯著手機看,也不需要時不時看一下手機,有短信回復會有通知,再來閱讀就好了。這就是 AIO 中的 AsynchronousServerSocketChannel,可以注冊一個回調 CompletionHandler,等待有消息的時候直接通知回調處理即可。
優缺點
AIO 包括了 NIO 的所有優缺點的同時,增加了異步回調的能力,由此解決的問題是不需要同步的等待非阻塞 IO 的反饋,所以 NIO 叫做異步非阻塞 IO 模型。
是時候展示真正的技術了
說了這么多,用 NIO 實現一個把妹聊天程序唄?
服務器端
public?class?NioServer?{public?void?start()?throws?IOException?{Selector?selector?=?Selector.open();ServerSocketChannel?serverSocketChannel?=?ServerSocketChannel.open();serverSocketChannel.bind(new?InetSocketAddress(6789));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector,?SelectionKey.OP_ACCEPT);System.out.println("服務器啟動成功");while?(true)?{selector.select();Set<SelectionKey>?selectionKeys?=?selector.selectedKeys();Iterator<SelectionKey>?iterator?=?selectionKeys.iterator();while?(iterator.hasNext())?{SelectionKey?selectionKey?=?iterator.next();if?(selectionKey.isAcceptable())?{iterator.remove();handleAccept(serverSocketChannel,?selector);}?else?if?(selectionKey.isReadable())?{handleRead(selectionKey);}?else?{System.out.println("其他請求");}}}}private?void?handleRead(SelectionKey?selectionKey)?throws?IOException?{SocketChannel?socketChannel?=?(SocketChannel)?selectionKey.channel();ByteBuffer?byteBuffer?=?ByteBuffer.allocate(1024);StringBuilder?request?=?new?StringBuilder();while?(socketChannel.read(byteBuffer)?>?0)?{byteBuffer.flip();request.append(StandardCharsets.UTF_8.decode(byteBuffer));}if?(request.length()?>?0)?{System.out.println("服務端收到消息:"?+?request.toString());}}private?void?handleAccept(ServerSocketChannel?serverSocketChannel,?Selector?selector)?throws?IOException?{SocketChannel?socketChannel?=?serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector,?SelectionKey.OP_READ);System.out.println("有新人進入聊天室");socketChannel.write(StandardCharsets.UTF_8.encode("進入聊天室,現在可以聊天了"));}public?static?void?main(String[]?args)?throws?IOException?{new?NioServer().start();} }客戶端
public?class?NioClient?{public?void?start()?throws?IOException?{SocketChannel?socketChannel?=?SocketChannel.open(new?InetSocketAddress(6789));Selector?selector?=?Selector.open();socketChannel.configureBlocking(false);socketChannel.register(selector,?SelectionKey.OP_READ);new?Thread(new?NioClientHandler(selector)).start();Scanner?scanner?=?new?Scanner(System.in);while?(scanner.hasNextLine())?{String?request?=?scanner.nextLine();if?(request?!=?null?&&?request.length()?>?0)?{socketChannel.write(StandardCharsets.UTF_8.encode(request));}}}public?static?void?main(String[]?args)?throws?IOException?{new?NioClient().start();}private?class?NioClientHandler?implements?Runnable?{private?Selector?selector;public?NioClientHandler(Selector?selector)?{this.selector?=?selector;}@Overridepublic?void?run()?{try?{while?(true)?{selector.select();Set<SelectionKey>?selectionKeys?=?selector.selectedKeys();Iterator?iterator?=?selectionKeys.iterator();while?(iterator.hasNext())?{SelectionKey?selectionKey?=?(SelectionKey)?iterator.next();SocketChannel?socketChannel?=?(SocketChannel)?selectionKey.channel();ByteBuffer?byteBuffer?=?ByteBuffer.allocate(1024);StringBuilder?response?=?new?StringBuilder();while?(socketChannel.read(byteBuffer)?>?0)?{byteBuffer.flip();response.append(StandardCharsets.UTF_8.decode(byteBuffer));}if?(response.length()?>?0)?{System.out.println("接收服務端消息:"?+?response);}}}}?catch?(Exception?e)?{e.printStackTrace();}}} }參考文檔
圖解 | 深入揭秘 epoll 是如何實現 IO 多路復用的!
解鎖網絡編程之NIO的前世今生
NIO如何實現多路復用?
深入理解 Java IO
高并發專題之 IO 多路復用:Select、Poll、Epoll
面試官問:前后端分離項目,有什么優缺點?我說:沒
2020 年騰訊新增 20 億行代碼,鵝廠第一編程語言還是它
通俗講解分布式鎖,看完不懂算我輸
寫博客能月入10K?
一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)
這或許是最美的Vue+Element開源后臺管理UI
推薦一款高顏值的 Spring Boot 快速開發框架
一款基于 Spring Boot 的現代化社區(論壇/問答/社交網絡/博客)
13K點贊都基于 Vue+Spring 前后端分離管理系統ELAdmin,大愛
想接私活時薪再翻一倍,建議根據這幾個開源的SpringBoot項目
總結
以上是生活随笔為你收集整理的第一次听人用男女关系讲 N(Non-Blocking)I(进)O(出),涨姿势了的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 把 Spring Cloud 给拆了!详
- 下一篇: 面试官:注解@Component,@Se