Java nio是在Java編程中對I/O的另外一種高級的抽象方式。其對I/O的使用方式也更加貼近操作系統(tǒng)使用I/O的方式:通道和字節(jié)緩沖。一般的用法可以參考<<Thinking in java>>。這里我們談?wù)勈褂胣io進(jìn)行網(wǎng)絡(luò)通信編程的特點(diǎn)。基本的Java socket編程對小規(guī)模的系統(tǒng)可以很好的工作,但是在資源不擴(kuò)展而用戶請求不斷增大的情況下,系統(tǒng)劣化的比率很大。我們通常會面臨這樣一些這些問題:
1、thread-per-request 的方式,這樣會有更多線程維護(hù)和切換的系統(tǒng)開銷,同時(shí)在系統(tǒng)擴(kuò)展性方面受到限制。
2、針對問題1,我們可以采用線程池的方式來節(jié)省線程創(chuàng)建、維護(hù)以及切換的開銷,但是這樣也會限制系統(tǒng)可以同時(shí)處理客戶端的數(shù)量,至少對一些長連接協(xié)議來說是這樣。此時(shí)增加線程池的大小是不能再提高系統(tǒng)性能的,而只會增加系統(tǒng)更多的線程開銷。
3、傳統(tǒng)的socket在讀/寫數(shù)據(jù)以及建立連接上都是阻塞式的(沒有數(shù)據(jù)可讀可能會阻塞;沒有足夠空間緩存?zhèn)鬏數(shù)臄?shù)據(jù)可能阻塞;服務(wù)端的accept方法以及socket構(gòu)造函數(shù)都會阻塞等待,直到連接建立) 。這些特性會降低整個(gè)系統(tǒng)對CPU的利用率以及系統(tǒng)的活躍性,降低系統(tǒng)對客戶請求的響應(yīng)性及響應(yīng)時(shí)間,在某些情況下是不可接受的甚至?xí)頌?zāi)難性的結(jié)果。在這樣的情形下,我們要解決cpu利用率、線程活躍性以及提高系統(tǒng)吞吐量,我們勢必做很多額外的工作且相當(dāng)復(fù)雜。
4、由于傳統(tǒng)socket的阻塞特性,每個(gè)對等端都在阻塞等待另外一端完成相關(guān)處理,這樣勢必增大死鎖的風(fēng)險(xiǎn)。
5、當(dāng)然為了提高系統(tǒng)的活躍性,傳統(tǒng)的socket在accept、構(gòu)造socket以及read方法上都增加了超時(shí)控制的處理以及使用socket.close方法來打斷一些阻塞操作,而網(wǎng)絡(luò)總是不確定的,設(shè)置這些超時(shí)時(shí)間的設(shè)置在具體的上下文環(huán)境中的取值也是比較復(fù)雜的。簡而言之,為了提高系統(tǒng)的活躍性會增大系統(tǒng)的復(fù)雜度。
那么,Java nio真的就是Java socket編程解決以上若干問題的靈丹妙藥嗎?答案是否定的。
nio的非阻塞特性是nio最大的特點(diǎn),所謂非阻塞無非就是將通信的信道設(shè)置成為非阻塞的情況下,對該信道的所有操作都是會立即返回的。如數(shù)據(jù)讀取,沒有數(shù)據(jù)的時(shí)候會返回0而不是阻塞在信道上。這樣就增大了系統(tǒng)的活躍性,使得系統(tǒng)不必浪費(fèi)資源的I/O操作上,系統(tǒng)可以更好的利用cpu資源做一些其它處理,在某些場景下會提高系統(tǒng)的響應(yīng)性以及吞吐量。
同時(shí)nio在網(wǎng)絡(luò)通信的中采用的網(wǎng)絡(luò)I/O事件驅(qū)動(dòng)的方式,即操作系統(tǒng)對用戶感興趣的通信信道及其上面的I/O事件進(jìn)行監(jiān)聽并通知應(yīng)用程序。這實(shí)際上也是觀察者模式的一種應(yīng)用,在這樣的背景下,問題4中的死鎖風(fēng)險(xiǎn)幾乎就沒有了。
如果你開發(fā)的服務(wù)端不是迭代服務(wù)器(順序化處理每個(gè)客戶請求),那么對于問題1、2在nio服務(wù)器中也是一樣的。換句話說,我們開發(fā)的并發(fā)處理服務(wù)器為了實(shí)現(xiàn)對各種網(wǎng)絡(luò)I/O事件的處理,并對每種我們所關(guān)心的網(wǎng)絡(luò)I/O事件進(jìn)行及時(shí)響應(yīng),采用thread-per-request或者線程池的方式是無法避免的。所以很多人會感覺nio的網(wǎng)絡(luò)編程結(jié)構(gòu)與非nio的網(wǎng)絡(luò)編程結(jié)構(gòu)實(shí)際是一樣的,accept換成了select,監(jiān)聽連接入站編程了監(jiān)聽信道上感興趣的I/O事件,為了提高系統(tǒng)的處理能力,還是要啟動(dòng)異步的線程來處理信道上的網(wǎng)絡(luò)I/O事件,只是具體實(shí)現(xiàn)不一樣而已。這說明nio并沒有改變我們服務(wù)端程序編寫的整體結(jié)構(gòu),只是在nio的環(huán)境下,我們的確可以去提高系統(tǒng)的活躍性、響應(yīng)性和吞吐量。nio與非nio在讀/寫網(wǎng)絡(luò)數(shù)據(jù)以及連接建立等網(wǎng)絡(luò)操作上是沒有多大區(qū)別的,這些因素主要都還是取決于網(wǎng)絡(luò)狀況、操作系統(tǒng)協(xié)議棧實(shí)現(xiàn)、應(yīng)用自身處理網(wǎng)絡(luò)數(shù)據(jù)的方式等多個(gè)方面。甚至,我們在享用nio的同時(shí)在一定程度上還會增大了網(wǎng)絡(luò)編程的復(fù)雜度,因?yàn)閿?shù)據(jù)的讀寫以及連接的建立等操作變得更加不確定,當(dāng)然最終網(wǎng)絡(luò)編程的復(fù)雜還是取決于協(xié)議的復(fù)雜度。