本篇內(nèi)容主要講解“Java NIO類庫Selector機(jī)制的原理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Java NIO類庫Selector機(jī)制的原理”吧!
10余年的東寶網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。營銷型網(wǎng)站的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整東寶建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。成都創(chuàng)新互聯(lián)從事“東寶網(wǎng)站設(shè)計(jì)”,“東寶網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
一、 前言
自從J2SE 1.4版本以來,JDK發(fā)布了全新的I/O類庫,簡(jiǎn)稱NIO,其不但引入了全新的高效的I/O機(jī)制,同時(shí),也引入了多路復(fù)用的異步模式。NIO的包中主要包含了這樣幾種抽象數(shù)據(jù)類型:
◆ Buffer:包含數(shù)據(jù)且用于讀寫的線形表結(jié)構(gòu)。其中還提供了一個(gè)特殊類用于內(nèi)存映射文件的I/O操作。
◆ Charset:它提供Unicode字符串影射到字節(jié)序列以及逆映射的操作。
◆ Channels:包含socket,file和pipe三種管道,都是全雙工的通道。
◆ Selector:多個(gè)異步I/O操作集中到一個(gè)或多個(gè)線程中(可以被看成是Unix中select()函數(shù)的面向?qū)ο蟀姹荆?/p>
我的大學(xué)同學(xué)趙錕在使用NIO類庫書寫相關(guān)網(wǎng)絡(luò)程序的時(shí)候,發(fā)現(xiàn)了一些Java異常RuntimeException,異常的報(bào)錯(cuò)信息讓他開始了對(duì)NIO的Selector進(jìn)行了一些調(diào)查。當(dāng)趙錕對(duì)我共享了Selector的一些底層機(jī)制的猜想和調(diào)查時(shí)候,我們覺得這是一件很有意思的事情,于是在伙同趙錕進(jìn)行過一系列的調(diào)查后,我倆發(fā)現(xiàn)了很多有趣的事情,于是導(dǎo)致了這篇文章的產(chǎn)生。這也是為什么本文的作者署名為我們兩人的原因。
先要說明的一點(diǎn)是,趙錕和我本質(zhì)上都是出身于Unix/Linux/C/C++的開發(fā)人員,對(duì)于Java,這并不是我們的長處,這篇文章本質(zhì)上出于對(duì)Java的Selector的好奇,因?yàn)閺谋砻嫔蟻砜碨elector似乎做到了一些讓我們這些C/C++出身的人比較驚奇的事情。
下面讓我來為你講述一下這段故事。
二、 故事開始 : 讓C++程序員寫Java程序!
沒有嚴(yán)重內(nèi)存問題,大量豐富的SDK類庫,超容易的跨平臺(tái),除了在性能上有些微辭,C++出身的程序員從來都不會(huì)覺得Java是一件很困難的事情。當(dāng)然,對(duì)于長期習(xí)慣于使用操作系統(tǒng)API(系統(tǒng)調(diào)用System Call)的C/C++程序來說,面對(duì)Java中的比較“另類”地操作系統(tǒng)資源的方法可能會(huì)略感困惑,但萬變不離其宗,只需要對(duì)面向?qū)ο蟮脑O(shè)計(jì)模式有一定的了解,用不了多長時(shí)間,Java的SDK類庫也能玩得隨心所欲。
在使用Java進(jìn)行相關(guān)網(wǎng)絡(luò)程序的的設(shè)計(jì)時(shí),出身C/C++的人,首先想到的框架就是多路復(fù)用,想到多路復(fù)用,Unix/Linux下馬上就能讓從想到select, poll, epoll系統(tǒng)調(diào)用。于是,在看到Java的NIO中的Selector類時(shí)必然會(huì)倍感親切。稍加查閱一下SDK手冊(cè)以及相關(guān)例程,不一會(huì)兒,一個(gè)多路復(fù)用的框架便呈現(xiàn)出來,隨手做個(gè)單元測(cè)試,沒啥問題,一切和C/C++照舊。然后告訴兄弟們,框架搞定,以后咱們就在Windows上開發(fā)及單元測(cè)試,完成后到運(yùn)行環(huán)境Unix上集成測(cè)試。心中并暗自念到,跨平臺(tái)就好啊,開發(fā)活動(dòng)都可以跨平臺(tái)了。
然而,好景不長,隨著代碼越來越多,邏輯越來越復(fù)雜。好好的框架居然在Windows上單元測(cè)試運(yùn)行開始出現(xiàn)異常,看著Java運(yùn)行異常出錯(cuò)的函數(shù)棧,異常居然由Selector.open()拋出,錯(cuò)誤信息居然是Unable to establish loopback connection。
“Selector.open()居然報(bào)loopback connection錯(cuò)誤,憑什么?不應(yīng)該???open的時(shí)候又沒有什么loopback的socket連接,怎么會(huì)報(bào)這個(gè)錯(cuò)?”
長期使用C/C++的程序當(dāng)然會(huì)對(duì)操作系統(tǒng)的調(diào)用非常熟悉,雖然Java的虛擬機(jī)搞的什么系統(tǒng)調(diào)用都不見了,但C/C++的程序員必然要比Java程序敏感許多。
三、 開始調(diào)查 : 怎么Java這么“傻”!
于是,C/C++的老鳥從SystemInternals上下載Process Explorer來查看一下究竟是什么個(gè)Loopback Connection。 果然,打開java運(yùn)行進(jìn)程,發(fā)現(xiàn)有一些自己連接自己的localhost的TCP/IP鏈接。于是另一個(gè)問題又出現(xiàn)了,“憑什么啊?為什么會(huì)有自己和自己的連接?我程序里沒有自己連接自己啊,怎么可能會(huì)有這樣的鏈接啊?而自己連接自己的端口號(hào)居然是些奇怪的端口?!?/p>
問題變得越來越蹊蹺了。難道這都是Selector.open()在做怪?難道Selector.open()要?jiǎng)?chuàng)建一個(gè)自己連接自己的鏈接?寫個(gè)程序看看:
import java.nio.channels.Selector; import java.lang.RuntimeException; import java.lang.Thread; public class TestSelector { private static final int MAXSIZE=5; public static final void main( String argc[] ) { Selector [] sels = new Selector[ MAXSIZE]; try{ for( int i = 0 ;i< MAXSIZE ;++i ) { sels[i] = Selector.open(); //sels[i].close(); } Thread.sleep(30000); }catch( Exception ex ){ throw new RuntimeException( ex ); } } }
這個(gè)程序什么也沒有,就是做5次Selector.open(),然后休息30秒,以便我使用Process Explorer工具來查看進(jìn)程。程序編譯沒有問題,運(yùn)行起來,在Process Explorer中看到下面的對(duì)話框:(居然有10個(gè)連接,從連接端口我們可以知道,互相連接, 如:***個(gè)連第二個(gè),第二個(gè)又連***個(gè))
不由得贊嘆我們的Java啊,先不說這是不是一件愚蠢的事。至少可以肯定的是,Java在消耗寶貴的系統(tǒng)資源方面,已經(jīng)可以趕的上某些蠕蟲病毒了。
如果不信,不妨把上面程序中的那個(gè)MAXSIZE的值改成65535試試,不一會(huì)你就會(huì)發(fā)現(xiàn)你的程序有這樣的錯(cuò)誤了:(在我的XP機(jī)器上大約運(yùn)行到2000個(gè)Selector.open() 左右)
Exception in thread "main" java.lang.RuntimeException: java.io.IOException: Unable to establish loopback connection at Test.main(Test.java:18) Caused by: java.io.IOException: Unable to establish loopback connection at sun.nio.ch.PipeImpl$Initializer.run(Unknown Source) at java.security.AccessController.doPrivileged(Native Method) at sun.nio.ch.PipeImpl.(Unknown Source) at sun.nio.ch.SelectorProviderImpl.openPipe(Unknown Source) at java.nio.channels.Pipe.open(Unknown Source) at sun.nio.ch.WindowsSelectorImpl.(Unknown Source) at sun.nio.ch.WindowsSelectorProvider.openSelector(Unknown Source) at java.nio.channels.Selector.open(Unknown Source) at Test.main(Test.java:15) Caused by: java.net.SocketException: No buffer space available (maximum connections reached?): connect at sun.nio.ch.Net.connect(Native Method) at sun.nio.ch.SocketChannelImpl.connect(Unknown Source) at java.nio.channels.SocketChannel.open(Unknown Source) ... 9 more
四、 繼續(xù)調(diào)查 : 如此跨平臺(tái)
當(dāng)然,沒人像我們這么變態(tài)寫出那么多的Selector.open(),但這正好可以讓我們來明白Java背著大家在干什么事。上面的那些“愚蠢連接”是在Windows平臺(tái)上,如果不出意外,Unix/Linux下應(yīng)該也差不多吧。
于是我們把上面的程序放在Linux下跑了跑。使用netstat 命令,并沒有看到自己和自己的Socket連接。貌似在Linux上使用了和Windows不一樣的機(jī)制?!
如果在Linux上不建自己和自己的TCP連接的話,那么文件描述符和端口都會(huì)被省下來了,是不是也就是說我們調(diào)用65535個(gè)Selector.open()的話,應(yīng)該不會(huì)出現(xiàn)異常了。
可惜,在實(shí)現(xiàn)運(yùn)行過程序當(dāng)中,還是一樣報(bào)錯(cuò):(大約在400個(gè)Selector.open()左右,還不如Windows)
Exception in thread "main" java.lang.RuntimeException: java.io.IOException: Too many open files at Test1.main(Test1.java:19) Caused by: java.io.IOException: Too many open files at sun.nio.ch.IOUtil.initPipe(Native Method) at sun.nio.ch.EPollSelectorImpl.(EPollSelectorImpl.java:49) at sun.nio.ch.EPollSelectorProvider.openSelector(EPollSelectorProvider.java:18) at java.nio.channels.Selector.open(Selector.java:209) at Test1.main(Test1.java:15)
我們發(fā)現(xiàn),這個(gè)異常錯(cuò)誤是“Too many open files”,于是我想到了使用lsof命令來查看一下打開的文件。
看到了有一些pipe文件,一共5對(duì),10個(gè)(當(dāng)然,管道從來都是成對(duì)的)。如下圖所示。
可見,Selector.open()在Linux下不用TCP連接,而是用pipe管道。看來,這個(gè)pipe管道也是自己給自己的。所以,我們可以得出下面的結(jié)論:
1)Windows下,Selector.open()會(huì)自己和自己建立兩條TCP鏈接。不但消耗了兩個(gè)TCP連接和端口,同時(shí)也消耗了文件描述符。
2)Linux下,Selector.open()會(huì)自己和自己建兩條管道。同樣消耗了兩個(gè)系統(tǒng)的文件描述符。
估計(jì),在Windows下,Sun的JVM之所以選擇TCP連接,而不是Pipe,要么是因?yàn)樾阅艿膯栴},要么是因?yàn)橘Y源的問題??赡埽琖indows下的管道的性能要慢于TCP鏈接,也有可能是Windows下的管道所消耗的資源會(huì)比TCP鏈接多。這些實(shí)現(xiàn)的細(xì)節(jié)還有待于更為深層次的挖掘。
到此,相信大家對(duì)“Java NIO類庫Selector機(jī)制的原理”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
網(wǎng)頁標(biāo)題:JavaNIO類庫Selector機(jī)制的原理
文章位置:http://jinyejixie.com/article16/gpecdg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、外貿(mào)網(wǎng)站建設(shè)、App設(shè)計(jì)、響應(yīng)式網(wǎng)站、動(dòng)態(tài)網(wǎng)站、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)