本篇內(nèi)容介紹了“Java NIO開(kāi)發(fā)的實(shí)例介紹”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
成都創(chuàng)新互聯(lián)主要從事網(wǎng)站制作、網(wǎng)站設(shè)計(jì)、網(wǎng)頁(yè)設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)石臺(tái),10年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來(lái)電咨詢建站服務(wù):13518219792
首先來(lái)看下傳統(tǒng)的阻塞型網(wǎng)絡(luò)I/O的不足
Java 平臺(tái)傳統(tǒng)的I/O 系統(tǒng)都是基于Byte(字節(jié))和Stream(數(shù)據(jù)流)的,相應(yīng)的I/O 操作都是阻塞型的,所以服務(wù)器程序也采用阻塞型I/O 進(jìn)行數(shù)據(jù)的讀、寫(xiě)操作。本文以TCP長(zhǎng)連接模式來(lái)討論并發(fā)型服務(wù)器的相關(guān)設(shè)計(jì),為了實(shí)現(xiàn)服務(wù)器程序的并發(fā)性要求,系統(tǒng)由一個(gè)單獨(dú)的主線程來(lái)監(jiān)聽(tīng)用戶發(fā)起的連接請(qǐng)求,一直處于阻塞狀態(tài);當(dāng)有用戶連接請(qǐng)求到來(lái)時(shí),程序都會(huì)啟一個(gè)新的線程來(lái)統(tǒng)一處理用戶數(shù)據(jù)的讀、寫(xiě)操作。
這種模式的優(yōu)點(diǎn)是簡(jiǎn)單、實(shí)用、易管理;然而缺點(diǎn)也是顯而易見(jiàn)的:由于是為每一個(gè)客戶端分配一個(gè)線程來(lái)處理輸入、輸出數(shù)據(jù),其線程與客戶機(jī)的比例近似為1:1,隨著線程數(shù)量的不斷增加,服務(wù)器啟動(dòng)了大量的并發(fā)線程,會(huì)大大加大系統(tǒng)對(duì)線程的管理開(kāi)銷,這將成為吞吐量瓶頸的主要原因;其次由于底層的I/O 操作采用的同步模式,I/O 操作的阻塞管理粒度是以服務(wù)于請(qǐng)求的線程為單位的,有可能大量的線程會(huì)閑置,處于盲等狀態(tài),造成I/O資源利用率不高,影響整個(gè)系統(tǒng)的性能。
對(duì)于并發(fā)型服務(wù)器,系統(tǒng)用在阻塞型I/O 等待和線程間切換的時(shí)間遠(yuǎn)遠(yuǎn)多于CPU 在內(nèi)存中處理數(shù)據(jù)的時(shí)間,因此傳統(tǒng)的阻塞型I/O 已經(jīng)成為制約系統(tǒng)性能的瓶頸。Java1.4 版本后推出的NIO 工具包,提供了非阻塞型I/O 的異步輸入輸出機(jī)制,為提高系統(tǒng)的性能提供了可實(shí)現(xiàn)的基礎(chǔ)機(jī)制。
NIO 包及工作原理
針對(duì)傳統(tǒng)I/O 工作模式的不足,NIO 工具包提出了基于Buffer(緩沖區(qū))、Channel(通道)、Selector(選擇器)的新模式;Selector(選擇器)、可選擇的Channel(通道)和SelectionKey(選擇鍵)配合起來(lái)使用,可以實(shí)現(xiàn)并發(fā)的非阻塞型I/O 能力。
NIO 工具包的成員
Buffer(緩沖器)
Buffer 類是一個(gè)抽象類,它有7 個(gè)子類分別對(duì)應(yīng)于七種基本的數(shù)據(jù)類型:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和ShortBuffer。每一個(gè)Buffer對(duì)象相當(dāng)于一個(gè)數(shù)據(jù)容器,可以把它看作內(nèi)存中的一個(gè)大的數(shù)組,用來(lái)存儲(chǔ)和提取所有基本類型(boolean 型除外)的數(shù)據(jù)。Buffer 類的核心是一塊內(nèi)存區(qū),可以直接對(duì)其執(zhí)行與內(nèi)存有關(guān)的操作,利用操作系統(tǒng)特性和能力提高和改善Java 傳統(tǒng)I/O 的性能。
Channel(通道)
Channel 被認(rèn)為是NIO 工具包的一大創(chuàng)新點(diǎn),是(Buffer)緩沖器和I/O 服務(wù)之間的通道,具有雙向性,既可以讀入也可以寫(xiě)出,可以更高效的傳遞數(shù)據(jù)。我們這里主要討ServerSocketChannel 和SocketChannel,它們都繼承了SelectableChannel,是可選擇的通道,分別可以工作在同步和異步兩種方式下(這里的可選擇不是指可以選擇兩種工作方式,而是指可以有選擇的注冊(cè)自己感興趣的事件)。當(dāng)通道工作在同步方式時(shí),它的功能和編程方法與傳統(tǒng)的ServerSocket、Socket 對(duì)象相似;當(dāng)通道工作在異步工作方式時(shí),進(jìn)行輸入輸出處理不必等到輸入輸出完畢才返回,并且可以將其感興趣的(如:接受操作、連接操作、讀出操作、寫(xiě)入操作)事件注冊(cè)到Selector 對(duì)象上,與Selector 對(duì)象協(xié)同工作可以更有效率的支持和管理并發(fā)的網(wǎng)絡(luò)套接字連接。
Selector(選擇器)和SelectionKey(選擇鍵)
各類 Buffer 是數(shù)據(jù)的容器對(duì)象;各類Channel 實(shí)現(xiàn)在各類Buffer 與各類I/O 服務(wù)間傳輸數(shù)據(jù)。Selector 是實(shí)現(xiàn)并發(fā)型非阻塞I/O 的核心,各種可選擇的通道將其感興趣的事件注冊(cè)到Selector 對(duì)象上,Selector 在一個(gè)循環(huán)中不斷輪循監(jiān)視這各些注冊(cè)在其上的Socket 通道。SelectionKey 類則封裝了SelectableChannel 對(duì)象在Selector 中的注冊(cè)信息。當(dāng)Selector 監(jiān)測(cè)到在某個(gè)注冊(cè)的SelectableChannel 上發(fā)生了感興趣的事件時(shí),自動(dòng)激活產(chǎn)生一個(gè)SelectionKey對(duì)象,在這個(gè)對(duì)象中記錄了哪一個(gè)SelectableChannel 上發(fā)生了哪種事件,通過(guò)對(duì)被激活的SelectionKey 的分析,外界可以知道每個(gè)SelectableChannel 發(fā)生的具體事件類型,進(jìn)行相應(yīng)的處理。
NIO 工作原理
通過(guò)上面的討論,我們可以看出在并發(fā)型服務(wù)器程序中使用NIO,實(shí)際上是通過(guò)網(wǎng)絡(luò)事件驅(qū)動(dòng)模型實(shí)現(xiàn)的。我們應(yīng)用Select 機(jī)制,不用為每一個(gè)客戶端連接新啟線程處理,而是將其注冊(cè)到特定的Selector 對(duì)象上,這就可以在單線程中利用Selector 對(duì)象管理大量并發(fā)的網(wǎng)絡(luò)連接,更好的利用了系統(tǒng)資源;采用非阻塞I/O 的通信方式,不要求阻塞等待I/O 操作完成即可返回,從而減少了管理I/O 連接導(dǎo)致的系統(tǒng)開(kāi)銷,大幅度提高了系統(tǒng)性能。
當(dāng)有讀或?qū)懙热魏巫?cè)的事件發(fā)生時(shí),可以從Selector 中獲得相應(yīng)的SelectionKey , 從SelectionKey 中可以找到發(fā)生的事件和該事件所發(fā)生的具體的SelectableChannel,以獲得客戶端發(fā)送過(guò)來(lái)的數(shù)據(jù)。由于在非阻塞網(wǎng)絡(luò)I/O 中采用了事件觸發(fā)機(jī)制,處理程序可以得到系統(tǒng)的主動(dòng)通知,從而可以實(shí)現(xiàn)底層網(wǎng)絡(luò)I/O 無(wú)阻塞、流暢地讀寫(xiě),而不像在原來(lái)的阻塞模式下處理程序需要不斷循環(huán)等待。使用NIO,可以編寫(xiě)出性能更好、更易擴(kuò)展的并發(fā)型服務(wù)器程序。
并發(fā)型服務(wù)器程序的實(shí)現(xiàn)代碼
應(yīng)用 NIO 工具包,基于非阻塞網(wǎng)絡(luò)I/O 設(shè)計(jì)的并發(fā)型服務(wù)器程序與以往基于阻塞I/O 的實(shí)現(xiàn)程序有很大不同,在使用非阻塞網(wǎng)絡(luò)I/O 的情況下,程序讀取數(shù)據(jù)和寫(xiě)入數(shù)據(jù)的時(shí)機(jī)不是由程序員控制的,而是Selector 決定的。下面便給出基于非阻塞網(wǎng)絡(luò)I/O 的并發(fā)型服務(wù)器程序的核心代碼片段:
import java.io.*; //引入Java.io包 import java.net.*; //引入Java.net包 import java.nio.channels.*; //引入Java.nio.channels包 import java.util.*; //引入Java.util包 public class TestServer implements Runnable { /** * 服務(wù)器Channel對(duì)象,負(fù)責(zé)接受用戶連接 */ private ServerSocketChannel server; /** * Selector對(duì)象,負(fù)責(zé)監(jiān)控所有的連接到服務(wù)器的網(wǎng)絡(luò)事件的發(fā)生 */ private Selector selector; /** * 總的活動(dòng)連接數(shù) */ private int activeSockets; /** * 服務(wù)器Channel綁定的端口號(hào) */ private int port ; /** * * 構(gòu)造函數(shù) */ public TestServer()throws IOException { activeSockets=0; port=9999;//初始化服務(wù)器Channel綁定的端口號(hào)為9999 selector= Selector.open();//初始化Selector對(duì)象 server=ServerSocketChannel.open();//初始化服務(wù)器Channel對(duì)象 ServerSocket socket=server.socket();//獲取服務(wù)器Channel對(duì)應(yīng)的//ServerSocket對(duì)象 socket.bind(new InetSocketAddress(port));//把Socket綁定到監(jiān)聽(tīng)端口9999上 server.configureBlocking(false);//將服務(wù)器Channel設(shè)置為非阻塞模式 server.register(selector,SelectionKey.OP_ACCEPT);//將服務(wù)器Channel注冊(cè)到 Selector對(duì)象,并指出服務(wù)器Channel所感興趣的事件為可接受請(qǐng)求操作 } public void run() { while(true) { try { /** *應(yīng)用Select機(jī)制輪循是否有用戶感興趣的新的網(wǎng)絡(luò)事件發(fā)生,當(dāng)沒(méi)有 * 新的網(wǎng)絡(luò)事件發(fā)生時(shí),此方法會(huì)阻塞,直到有新的網(wǎng)絡(luò)事件發(fā)生為止 */ selector.select(); } catch(IOException e) { continue;//當(dāng)有異常發(fā)生時(shí),繼續(xù)進(jìn)行循環(huán)操作 } /** * 得到活動(dòng)的網(wǎng)絡(luò)連接選擇鍵的集合 */ Set<SelectionKey> keys=selector.selectedKeys(); activeSockets=keys.size();//獲取活動(dòng)連接的數(shù)目 if(activeSockets==0) { continue;//如果連接數(shù)為0,則繼續(xù)進(jìn)行循環(huán)操作 } /** /** * 應(yīng)用For—Each循環(huán)遍歷整個(gè)選擇鍵集合 */ for(SelectionKey key :keys) { /** * 如果關(guān)鍵字狀態(tài)是為可接受,則接受連接,注冊(cè)通道,以接受更多的* 事件,進(jìn)行相關(guān)的服務(wù)器程序處理 */ if(key.isAcceptable()) { doServerSocketEvent(key); continue; } /** * 如果關(guān)鍵字狀態(tài)為可讀,則說(shuō)明Channel是一個(gè)客戶端的連接通道, * 進(jìn)行相應(yīng)的讀取客戶端數(shù)據(jù)的操作 */ if(key.isReadable()) { doClientReadEvent(key); continue; } /** * 如果關(guān)鍵字狀態(tài)為可寫(xiě),則也說(shuō)明Channel是一個(gè)客戶端的連接通道, * 進(jìn)行相應(yīng)的向客戶端寫(xiě)數(shù)據(jù)的操作 */ if(key.isWritable()) { doClinetWriteEvent(key); continue; } } } } /** * 處理服務(wù)器事件操作 * @param key 服務(wù)器選擇鍵對(duì)象 */ private void doServerSocketEvent(SelectionKey key) { SocketChannel client=null; try { ServerSocketChannel server=(ServerSocketChannel)key.channel(); client=server.accept(); if(client==null) { return; } client.configureBlocking(false);//將客戶端Channel設(shè)置為非阻塞型 /** /** * 將客戶端Channel注冊(cè)到Selector對(duì)象上,并且指出客戶端Channel所感 * 興趣的事件為可讀和可寫(xiě) */ client.register(selector,SelectionKey.OP_READ|SelectionKey.OP_READ); }catch(IOException e) { try { client.close(); }catch(IOException e1){} } } /** * 進(jìn)行向客戶端寫(xiě)數(shù)據(jù)操作 * @param key 客戶端選擇鍵對(duì)象 */ private void doClinetWriteEvent(SelectionKey key) { 代碼實(shí)現(xiàn)略; } /** * 進(jìn)行讀取客戶短數(shù)據(jù)操作 * @param key 客戶端選擇鍵對(duì)象 */ private void doClientReadEvent(SelectionKey key) { 代碼實(shí)現(xiàn)略; } }
從上面對(duì)代碼可以看出,使用非阻塞性I/O進(jìn)行并發(fā)型服務(wù)器程序設(shè)計(jì)分三個(gè)部分:1.向Selector對(duì)象注冊(cè)感興趣的事件;2.從Selector中獲取所感興趣的事件;3.根據(jù)不同的事件進(jìn)行相應(yīng)的處理。
“Java NIO開(kāi)發(fā)的實(shí)例介紹”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
網(wǎng)站名稱:JavaNIO開(kāi)發(fā)的實(shí)例介紹
網(wǎng)頁(yè)鏈接:http://chinadenli.net/article22/gjeocc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、手機(jī)網(wǎng)站建設(shè)、域名注冊(cè)、響應(yīng)式網(wǎng)站、網(wǎng)站策劃、云服務(wù)器
聲明:本網(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í)需注明來(lái)源: 創(chuàng)新互聯(lián)