Java NIO原理 圖文分析及代碼實現

原文出處:http://weixiaolu.iteye.com/blog/1479656

----------------------------------------------------------------------

?Java NIO原理圖文分析及代碼實現?
前言:?

最近在分析hadoop的RPC(Remote Procedure Call Protocol ,遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡技術的協議。可以參考:http://baike.baidu.com/view/32726.htm?)機制時,發現hadoop的RPC機制的實現主要用到了兩個技術:動態代理(動態代理可以參考博客:http://weixiaolu.iteye.com/blog/1477774?)和java NIO。為了能夠正確地分析hadoop的RPC源碼,我覺得很有必要先研究一下java NIO的原理和具體實現。

這篇博客我主要從兩個方向來分析java NIO

目錄:
一.java NIO 和阻塞I/O的區別
???? 1. 阻塞I/O通信模型
???? 2. java NIO原理及通信模型
二.java NIO服務端和客戶端代碼實現
?

具體分析:?

一.java NIO 和阻塞I/O的區別?

1. 阻塞I/O通信模型
?

假如現在你對阻塞I/O已有了一定了解,我們知道阻塞I/O在調用InputStream.read()方法時是阻塞的,它會一直等到數據到來時(或超時)才會返回;同樣,在調用ServerSocket.accept()方法時,也會一直阻塞到有客戶端連接才會返回,每個客戶端連接過來后,服務端都會啟動一個線程去處理該客戶端的請求。阻塞I/O的通信模型示意圖如下:

?

?

如果你細細分析,一定會發現阻塞I/O存在一些缺點。根據阻塞I/O通信模型,我總結了它的兩點缺點:
1. 當客戶端多時,會創建大量的處理線程。且每個線程都要占用棧空間和一些CPU時間

2. 阻塞可能帶來頻繁的上下文切換,且大部分上下文切換可能是無意義的。

在這種情況下非阻塞式I/O就有了它的應用前景。

2.?
java NIO原理及通信模型?

Java NIO是在jdk1.4開始使用的,它既可以說成“新I/O”,也可以說成非阻塞式I/O。下面是java NIO的工作原理:

1. 由一個專門的線程來處理所有的 IO 事件,并負責分發。?
2. 事件驅動機制:事件到的時候觸發,而不是同步的去監視事件。?
3. 線程通訊:線程之間通過 wait,notify 等方式通訊。保證每次上下文切換都是有意義的。減少無謂的線程切換。?

閱讀過一些資料之后,下面貼出我理解的java NIO的工作原理圖:

?

?

(注:每個線程的處理流程大概都是讀取數據、解碼、計算處理、編碼、發送響應。)

Java NIO的服務端只需啟動一個專門的線程來處理所有的 IO 事件,這種通信模型是怎么實現的呢?呵呵,我們一起來探究它的奧秘吧。java NIO采用了雙向通道(channel)進行數據傳輸,而不是單向的流(stream),在通道上可以注冊我們感興趣的事件。一共有以下四種事件:

?

事件名對應值
服務端接收客戶端連接事件SelectionKey.OP_ACCEPT(16)
客戶端連接服務端事件SelectionKey.OP_CONNECT(8)
讀事件SelectionKey.OP_READ(1)
寫事件SelectionKey.OP_WRITE(4)

?

??
??
??
??
?服務端和客戶端各自維護一個管理通道的對象,我們稱之為selector,該對象能檢測一個或多個通道 (channel) 上的事件。我們以服務端為例,如果服務端的selector上注冊了讀事件,某時刻客戶端給服務端發送了一些數據,阻塞I/O這時會調用read()方法阻塞地讀取數據,而NIO的服務端會在selector中添加一個讀事件。服務端的處理線程會輪詢地訪問selector,如果訪問selector時發現有感興趣的事件到達,則處理這些事件,如果沒有感興趣的事件到達,則處理線程會一直阻塞直到感興趣的事件到達為止。下面是我理解的java NIO的通信模型示意圖:

?

?

二.java NIO服務端和客戶端代碼實現?

為了更好地理解java NIO,下面貼出服務端和客戶端的簡單代碼實現。

服務端:

?

Java代碼??收藏代碼
  1. package?cn.nio;??
  2. ??
  3. import?java.io.IOException;??
  4. import?java.net.InetSocketAddress;??
  5. import?java.nio.ByteBuffer;??
  6. import?java.nio.channels.SelectionKey;??
  7. import?java.nio.channels.Selector;??
  8. import?java.nio.channels.ServerSocketChannel;??
  9. import?java.nio.channels.SocketChannel;??
  10. import?java.util.Iterator;??
  11. ??
  12. /**?
  13. ?*?NIO服務端?
  14. ?*?@author?小路?
  15. ?*/??
  16. public?class?NIOServer?{??
  17. ????//通道管理器??
  18. ????private?Selector?selector;??
  19. ??
  20. ????/**?
  21. ?????*?獲得一個ServerSocket通道,并對該通道做一些初始化的工作?
  22. ?????*?@param?port??綁定的端口號?
  23. ?????*?@throws?IOException?
  24. ?????*/??
  25. ????public?void?initServer(int?port)?throws?IOException?{??
  26. ????????//?獲得一個ServerSocket通道??
  27. ????????ServerSocketChannel?serverChannel?=?ServerSocketChannel.open();??
  28. ????????//?設置通道為非阻塞??
  29. ????????serverChannel.configureBlocking(false);??
  30. ????????//?將該通道對應的ServerSocket綁定到port端口??
  31. ????????serverChannel.socket().bind(new?InetSocketAddress(port));??
  32. ????????//?獲得一個通道管理器??
  33. ????????this.selector?=?Selector.open();??
  34. ????????//將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_ACCEPT事件,注冊該事件后,??
  35. ????????//當該事件到達時,selector.select()會返回,如果該事件沒到達selector.select()會一直阻塞。??
  36. ????????serverChannel.register(selector,?SelectionKey.OP_ACCEPT);??
  37. ????}??
  38. ??
  39. ????/**?
  40. ?????*?采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理?
  41. ?????*?@throws?IOException?
  42. ?????*/??
  43. ????@SuppressWarnings("unchecked")??
  44. ????public?void?listen()?throws?IOException?{??
  45. ????????System.out.println("服務端啟動成功!");??
  46. ????????//?輪詢訪問selector??
  47. ????????while?(true)?{??
  48. ????????????//當注冊的事件到達時,方法返回;否則,該方法會一直阻塞??
  49. ????????????selector.select();??
  50. ????????????//?獲得selector中選中的項的迭代器,選中的項為注冊的事件??
  51. ????????????Iterator?ite?=?this.selector.selectedKeys().iterator();??
  52. ????????????while?(ite.hasNext())?{??
  53. ????????????????SelectionKey?key?=?(SelectionKey)?ite.next();??
  54. ????????????????//?刪除已選的key,以防重復處理??
  55. ????????????????ite.remove();??
  56. ????????????????//?客戶端請求連接事件??
  57. ????????????????if?(key.isAcceptable())?{??
  58. ????????????????????ServerSocketChannel?server?=?(ServerSocketChannel)?key??
  59. ????????????????????????????.channel();??
  60. ????????????????????//?獲得和客戶端連接的通道??
  61. ????????????????????SocketChannel?channel?=?server.accept();??
  62. ????????????????????//?設置成非阻塞??
  63. ????????????????????channel.configureBlocking(false);??
  64. ??
  65. ????????????????????//在這里可以給客戶端發送信息哦??
  66. ????????????????????channel.write(ByteBuffer.wrap(new?String("向客戶端發送了一條信息").getBytes()));??
  67. ????????????????????//在和客戶端連接成功之后,為了可以接收到客戶端的信息,需要給通道設置讀的權限。??
  68. ????????????????????channel.register(this.selector,?SelectionKey.OP_READ);??
  69. ??????????????????????
  70. ????????????????????//?獲得了可讀的事件??
  71. ????????????????}?else?if?(key.isReadable())?{??
  72. ????????????????????????read(key);??
  73. ????????????????}??
  74. ??
  75. ????????????}??
  76. ??
  77. ????????}??
  78. ????}??
  79. ????/**?
  80. ?????*?處理讀取客戶端發來的信息?的事件?
  81. ?????*?@param?key?
  82. ?????*?@throws?IOException??
  83. ?????*/??
  84. ????public?void?read(SelectionKey?key)?throws?IOException{??
  85. ????????//?服務器可讀取消息:得到事件發生的Socket通道??
  86. ????????SocketChannel?channel?=?(SocketChannel)?key.channel();??
  87. ????????//?創建讀取的緩沖區??
  88. ????????ByteBuffer?buffer?=?ByteBuffer.allocate(10);??
  89. ????????channel.read(buffer);??
  90. ????????byte[]?data?=?buffer.array();??
  91. ????????String?msg?=?new?String(data).trim();??
  92. ????????System.out.println("服務端收到信息:"+msg);??
  93. ????????ByteBuffer?outBuffer?=?ByteBuffer.wrap(msg.getBytes());??
  94. ????????channel.write(outBuffer);//?將消息回送給客戶端??
  95. ????}??
  96. ??????
  97. ????/**?
  98. ?????*?啟動服務端測試?
  99. ?????*?@throws?IOException??
  100. ?????*/??
  101. ????public?static?void?main(String[]?args)?throws?IOException?{??
  102. ????????NIOServer?server?=?new?NIOServer();??
  103. ????????server.initServer(8000);??
  104. ????????server.listen();??
  105. ????}??
  106. ??
  107. }??

?

?

客戶端:

?

?

Java代碼??收藏代碼
  1. package?cn.nio;??
  2. ??
  3. import?java.io.IOException;??
  4. import?java.net.InetSocketAddress;??
  5. import?java.nio.ByteBuffer;??
  6. import?java.nio.channels.SelectionKey;??
  7. import?java.nio.channels.Selector;??
  8. import?java.nio.channels.SocketChannel;??
  9. import?java.util.Iterator;??
  10. ??
  11. /**?
  12. ?*?NIO客戶端?
  13. ?*?@author?小路?
  14. ?*/??
  15. public?class?NIOClient?{??
  16. ????//通道管理器??
  17. ????private?Selector?selector;??
  18. ??
  19. ????/**?
  20. ?????*?獲得一個Socket通道,并對該通道做一些初始化的工作?
  21. ?????*?@param?ip?連接的服務器的ip?
  22. ?????*?@param?port??連接的服務器的端口號??????????
  23. ?????*?@throws?IOException?
  24. ?????*/??
  25. ????public?void?initClient(String?ip,int?port)?throws?IOException?{??
  26. ????????//?獲得一個Socket通道??
  27. ????????SocketChannel?channel?=?SocketChannel.open();??
  28. ????????//?設置通道為非阻塞??
  29. ????????channel.configureBlocking(false);??
  30. ????????//?獲得一個通道管理器??
  31. ????????this.selector?=?Selector.open();??
  32. ??????????
  33. ????????//?客戶端連接服務器,其實方法執行并沒有實現連接,需要在listen()方法中調??
  34. ????????//用channel.finishConnect();才能完成連接??
  35. ????????channel.connect(new?InetSocketAddress(ip,port));??
  36. ????????//將通道管理器和該通道綁定,并為該通道注冊SelectionKey.OP_CONNECT事件。??
  37. ????????channel.register(selector,?SelectionKey.OP_CONNECT);??
  38. ????}??
  39. ??
  40. ????/**?
  41. ?????*?采用輪詢的方式監聽selector上是否有需要處理的事件,如果有,則進行處理?
  42. ?????*?@throws?IOException?
  43. ?????*/??
  44. ????@SuppressWarnings("unchecked")??
  45. ????public?void?listen()?throws?IOException?{??
  46. ????????//?輪詢訪問selector??
  47. ????????while?(true)?{??
  48. ????????????selector.select();??
  49. ????????????//?獲得selector中選中的項的迭代器??
  50. ????????????Iterator?ite?=?this.selector.selectedKeys().iterator();??
  51. ????????????while?(ite.hasNext())?{??
  52. ????????????????SelectionKey?key?=?(SelectionKey)?ite.next();??
  53. ????????????????//?刪除已選的key,以防重復處理??
  54. ????????????????ite.remove();??
  55. ????????????????//?連接事件發生??
  56. ????????????????if?(key.isConnectable())?{??
  57. ????????????????????SocketChannel?channel?=?(SocketChannel)?key??
  58. ????????????????????????????.channel();??
  59. ????????????????????//?如果正在連接,則完成連接??
  60. ????????????????????if(channel.isConnectionPending()){??
  61. ????????????????????????channel.finishConnect();??
  62. ??????????????????????????
  63. ????????????????????}??
  64. ????????????????????//?設置成非阻塞??
  65. ????????????????????channel.configureBlocking(false);??
  66. ??
  67. ????????????????????//在這里可以給服務端發送信息哦??
  68. ????????????????????channel.write(ByteBuffer.wrap(new?String("向服務端發送了一條信息").getBytes()));??
  69. ????????????????????//在和服務端連接成功之后,為了可以接收到服務端的信息,需要給通道設置讀的權限。??
  70. ????????????????????channel.register(this.selector,?SelectionKey.OP_READ);??
  71. ??????????????????????
  72. ????????????????????//?獲得了可讀的事件??
  73. ????????????????}?else?if?(key.isReadable())?{??
  74. ????????????????????????read(key);??
  75. ????????????????}??
  76. ??
  77. ????????????}??
  78. ??
  79. ????????}??
  80. ????}??
  81. ????/**?
  82. ?????*?處理讀取服務端發來的信息?的事件?
  83. ?????*?@param?key?
  84. ?????*?@throws?IOException??
  85. ?????*/??
  86. ????public?void?read(SelectionKey?key)?throws?IOException{??
  87. ????????//和服務端的read方法一樣??
  88. ????}??
  89. ??????
  90. ??????
  91. ????/**?
  92. ?????*?啟動客戶端測試?
  93. ?????*?@throws?IOException??
  94. ?????*/??
  95. ????public?static?void?main(String[]?args)?throws?IOException?{??
  96. ????????NIOClient?client?=?new?NIOClient();??
  97. ????????client.initClient("localhost",8000);??
  98. ????????client.listen();??
  99. ????}??
  100. ??
  101. }??

?

?

小結:?

終于把動態代理和java NIO分析完了,呵呵,下面就要分析hadoop的RPC機制源碼了,博客地址:http://weixiaolu.iteye.com/blog/1504898?。不過如果對java NIO的理解存在異議的,歡迎一起討論。




本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/539699.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539699.shtml
英文地址,請注明出處:http://en.pswp.cn/news/539699.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

威綸通觸摸屏與mysql_威綸觸摸屏應用實例 以及威綸通觸摸屏配方組合

在生產車間,每臺設備運作起來都會有各自的組合參數,每臺設備運作的時候產生的數據即構成配方表,每張配方表內可建立多組配方數據,這些多臺機器的配方數據都可儲存在人機里,以供控制器(PLC)執行相對應的任務。從“包裝機…

分析師視角:2018年的數據中心3大預測

向數字業務平臺的遷移促使基礎設施和運營(I&O)領導者必須重新去思考他們的數據中心策略。像人工智能這樣的數字業務平臺,包括機器學習(ML)、深度神經網絡(DNN)和物聯網,對IT基礎設施有敏捷和可伸縮性等方面的計算需求。在2018年,I&O領…

大數據實時處理:百分點實時計算架構和算法

原文:http://www.oschina.net/question/1459174_145255 百分點官網:http://www.baifendian.com/ ------------------以下正文---------------------- 當今時代,數據不再昂貴,但從海量數據中獲取價值變得昂貴,而要及時…

ELK 日志處理開發指南

ELK 是 Elastic 公司出品的開源實時日志處理與分析解決方案,ELK 分別代表分布式搜索引擎 Elasticsearch、日志采集與解析工具 Logstash、日志可視化分析工具Kibana,具有配置方式靈活、集群可線性擴展、日志實時導入、檢索性能高效、可視化分析方便等優點…

UNION 和UNION ALL 的區別

UNION:的結果集沒有重復行,且安union默認的排序規則進行排序了。 UNION ALL:的結果集,如果各表有重復行就有重復行,不刪重復行,不排序。 ------------------------- 在數據庫中,UNION和UNION…

mysql connector 教程_MySQL Connector/C++入門教程(上)

目錄MySQL C Driver的實現基于JDBC4.0規范安裝MySQL Connector/C運行時依賴C IDE為示例程序創建數據庫與數據表使用Connector/C測試數據庫連接使用prepared Statements使用事務訪問Result Set Metadata訪問Database Metadata通過PreparedStatment對象訪問參數元數據捕獲異常調試…

14-項目開發總結報告(GB8567——88)

項目開發總結報告(GB8567——88)1引言1.1編寫目的說明編寫這份項目開發總結報告的目的,指出預期的閱讀范圍。1.2背景說明:a. 本項目的名稱和所開發出來的軟件系統的名稱;b. 此軟件的任務提出者、…

Oracle 并行查詢

所謂并行執行,是指能夠將一個大型串行任務(任何DML,一般的DDL)物理的劃分為叫多個小的部分,這些較小的部分可以同時得到處理。 何時使用并行執行: 1、必須有一個非常大的任務 2、必須有充足的資源(CPU,I…

python中求二維數組元素之和_python二維列表求解所有元素之和

相信很多初學小伙伴都會遇到二維列表求解所有元素之和問題,下面給出兩種兩種常見的求和方法。 方法1: 思想:遍歷整個二維列表元素,然后將所有元素加起來 1 def Sum_matrix(matrix): 2 sum=0 3 for i in range(len(matrix)): 4 for j in range(len(matrix[i])): 5 sum+=matr…

maven 國內私服

2019獨角獸企業重金招聘Python工程師標準>>> <repositories> <repository> <id>aliyun-cache</id> <name>aliyun-cache</name> <url>http://maven.aliyun.com/nexus/content/groups/public&…

mysql添加約束之前不滿足_MySQL:添加約束(如果不存在)

小編典典有趣的問題。您可能需要在調用CREATE TABLE語句之前禁用外鍵&#xff0c;然后再啟用它們。這將允許您直接在CREATE TABLEDDL中定義外鍵&#xff1a;例&#xff1a;SET FOREIGN_KEY_CHECKS 0;Query OK, 0 rows affected (0.00 sec)CREATE TABLE IF NOT EXISTS rabbits …

oracle函數trunc的使用

原文&#xff1a;http://blog.csdn.net/eleven204/article/details/6712538 -------------------------------------- 1、日期比較時精確到日&#xff0c;可以使用 TRUNC(sysdate,dd)函數。 函數支持格式有&#xff1a;yyyy MM dd hh Mi&#xff0c;沒有精確到 秒 可以用 se…

Mycat快速入門

1.Mycat介紹 Mycat 是一個開源的分布式數據庫系統&#xff0c;是一個實現了 MySQL 協議的的Server&#xff0c;前端用戶可以把它看作是一個數據庫代理&#xff0c;用 MySQL 客戶端工具和命令行訪問&#xff0c;而其后端可以用MySQL 原生&#xff08;Native&#xff09;協議與多…

python字符串常量有什么區別_Python經典面試題:is與==的區別

is用于判斷兩個對象是否為同一個對象&#xff0c;具體來說是兩個對象在內存中的位置是否相同。python為了提高效率&#xff0c;節省內存&#xff0c;在實現上大量使用了緩沖池技術和字符串intern技術。整數和字符串是不可變對象&#xff0c;也就意味著可以用來共享&#xff0c;…

left join、right join、inner join的區別

left join(左聯接) 返回包括左表中的所有記錄和右表中聯結字段相等的記錄 right join(右聯接) 返回包括右表中的所有記錄和左表中聯結字段相等的記錄 inner join(等值連接) 只返回兩個表中聯結字段相等的行 舉例如下&#xff1a; ----------------------------------------…

Javascript Proxy對象 簡介

Javascript Proxy對象 簡介 本文轉載自&#xff1a;眾成翻譯 譯者&#xff1a;eJayYoung 鏈接&#xff1a;http://www.zcfy.cc/article/4755 原文&#xff1a;https://blog.campvanilla.com/advanced-guide-javascript-proxy-objects-introduction-301c0fce9432 Javascript …

App架構經驗總結

原文地址&#xff1a;http://www.iteye.com/news/31472-------------------------------------------------------------架構因人而異&#xff0c;不同的架構師大多會有不同的看法&#xff1b;架構也因項目而異&#xff0c;不同的項目需求不同&#xff0c;相應的架構也會不同。…

python數字排序 循環_【python-leetcode448-循環排序】找到所有數組中消失的數字

問題描述&#xff1a;給定一個范圍在 1 ≤ a[i] ≤ n ( n 數組大小 ) 的 整型數組&#xff0c;數組中的元素一些出現了兩次&#xff0c;另一些只出現一次。找到所有在 [1, n] 范圍之間沒有出現在數組中的數字。您能在不使用額外空間且時間復雜度為O(n)的情況下完成這個任務嗎…

saiku+kettle整合(六)olap操作

title: saikukettle整合&#xff08;六&#xff09;olap操作 tags: categories: saiku date: 2016-08-25 18:18:54 使用saiku可以對應使用相關olap操作 OLAP的基本操作 我們已經知道OLAP的操作是以查詢——也就是數據庫的SELECT操作為主&#xff0c;但是查詢可以很復雜&#xf…

攜程Docker實踐

原文地址&#xff1a;http://www.iteye.com/news/31468 請點擊原文閱讀 ---------------------以下是原文---------------------- 從去年底開始&#xff0c;攜程開始計劃把Docker引入到攜程的云平臺&#xff0c;這是系統研發部一部分的工作任務&#xff0c;攜程系統研…