熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

java nio 開發實例

2013-11-23 19:36:56  來源: Java核心技術 

  首先了解下所謂的java nio是個什麼東西!

  傳統的並發型服務器設計是利用阻塞型網絡I/O 以多線程的模式來實現的然而由

  於系統常常在進行網絡讀寫時處於阻塞狀態會大大影響系統的性能自Java 開始引入

  了NIO(新I/O) API通過使用非阻塞型I/O實現流暢的網絡讀寫操作為開發高性能並發

  型服務器程序提供了一個很好的解決方案這就是java nio

  首先來看下傳統的阻塞型網絡 I/O的不足

  Java 平台傳統的I/O 系統都是基於Byte(字節)和Stream(數據流)的相應的I/O 操

  作都是阻塞型的所以服務器程序也采用阻塞型I/O 進行數據的讀寫操作本文以TCP

  長連接模式來討論並發型服務器的相關設計為了實現服務器程序的並發性要求系統由一

  個單獨的主線程來監聽用戶發起的連接請求一直處於阻塞狀態當有用戶連接請求到來時

  程序都會啟一個新的線程來統一處理用戶數據的讀寫操作

  這種模式的優點是簡單實用易管理然而缺點也是顯而易見的由於是為每一個客

  戶端分配一個線程來處理輸入輸出數據其線程與客戶機的比例近似為隨著線程

  數量的不斷增加服務器啟動了大量的並發線程會大大加大系統對線程的管理開銷這將

  成為吞吐量瓶頸的主要原因其次由於底層的I/O 操作采用的同步模式I/O 操作的阻塞管

  理粒度是以服務於請求的線程為單位的有可能大量的線程會閒置處於盲等狀態造成I/O

  資源利用率不高影響整個系統的性能

  對於並發型服務器系統用在阻塞型I/O 等待和線程間切換的時間遠遠多於CPU 在內

  存中處理數據的時間因此傳統的阻塞型I/O 已經成為制約系統性能的瓶頸Java 版本

  後推出的NIO 工具包提供了非阻塞型I/O 的異步輸入輸出機制為提高系統的性能提供

  了可實現的基礎機制

  NIO 包及工作原理

  針對傳統I/O 工作模式的不足NIO 工具包提出了基於Buffer(緩沖區)Channel(通

  道)Selector(選擇器)的新模式Selector(選擇器)可選擇的Channel(通道)和

  SelectionKey(選擇鍵)配合起來使用可以實現並發的非阻塞型I/O 能力

  NIO 工具包的成員

  Buffer(緩沖器)

  Buffer 類是一個抽象類它有 個子類分別對應於七種基本的數據類型ByteBuffer

  CharBufferDoubleBufferFloatBufferIntBufferLongBuffer 和ShortBuffer每一個Buffer

  對象相當於一個數據容器可以把它看作內存中的一個大的數組用來存儲和提取所有基本

  類型(boolean 型除外)的數據Buffer 類的核心是一塊內存區可以直接對其執行與內存有關

  的操作利用操作系統特性和能力提高和改善Java 傳統I/O 的性能

  Channel(通道)

  Channel 被認為是NIO 工具包的一大創新點是(Buffer)緩沖器和I/O 服務之間的通道

  具有雙向性既可以讀入也可以寫出可以更高效的傳遞數據我們這裡主要討論

  ServerSocketChannel 和SocketChannel它們都繼承了SelectableChannel是可選擇的通道

  分別可以工作在同步和異步兩種方式下(這裡的可選擇不是指可以選擇兩種工作方式而是

  指可以有選擇的注冊自己感興趣的事件)當通道工作在同步方式時它的功能和編程方法

  與傳統的ServerSocketSocket 對象相似當通道工作在異步工作方式時進行輸入輸出處

  理不必等到輸入輸出完畢才返回並且可以將其感興趣的(如接受操作連接操作讀出

  操作寫入操作)事件注冊到Selector 對象上與Selector 對象協同工作可以更有效率的支

  持和管理並發的網絡套接字連接

  Selector(選擇器)和SelectionKey(選擇鍵)

  各類 Buffer 是數據的容器對象各類Channel 實現在各類Buffer 與各類I/O 服務間傳輸

  數據Selector 是實現並發型非阻塞I/O 的核心各種可選擇的通道將其感興趣的事件注冊

  到Selector 對象上Selector 在一個循環中不斷輪循監視這各些注冊在其上的Socket 通道

  SelectionKey 類則封裝了SelectableChannel 對象在Selector 中的注冊信息當Selector 監測

  到在某個注冊的SelectableChannel 上發生了感興趣的事件時自動激活產生一個SelectionKey

  對象在這個對象中記錄了哪一個SelectableChannel 上發生了哪種事件通過對被激活的

  SelectionKey 的分析外界可以知道每個SelectableChannel 發生的具體事件類型進行相應的

  處理

  NIO 工作原理

  通過上面的討論我們可以看出在並發型服務器程序中使用NIO實際上是通過網絡事

  件驅動模型實現的我們應用Select 機制不用為每一個客戶端連接新啟線程處理而是將

  其注冊到特定的Selector 對象上這就可以在單線程中利用Selector 對象管理大量並發的網

  絡連接更好的利用了系統資源采用非阻塞I/O 的通信方式不要求阻塞等待I/O 操作完

  成即可返回從而減少了管理I/O 連接導致的系統開銷大幅度提高了系統性能

  當有讀或寫等任何注冊的事件發生時可以從Selector 中獲得相應的

  SelectionKey 從SelectionKey 中可以找到發生的事件和該事件所發生的具體的

  SelectableChannel以獲得客戶端發送過來的數據由於在非阻塞網絡I/O 中采用了事件觸

  發機制處理程序可以得到系統的主動通知從而可以實現底層網絡I/O 無阻塞流暢地讀

  寫而不像在原來的阻塞模式下處理程序需要不斷循環等待使用NIO可以編寫出性能更

  好更易擴展的並發型服務器程序

  並發型服務器程序的實現代碼

  應用 NIO 工具包基於非阻塞網絡I/O 設計的並發型服務器程序與以往基於阻塞I/O 的

  實現程序有很大不同在使用非阻塞網絡I/O 的情況下程序讀取數據和寫入數據的時機不

  是由程序員控制的而是Selector 決定的下面便給出基於非阻塞網絡I/O 的並發型服務器

  程序的核心代碼片段

  import javaio* //引入Javaio包

  import * //引入包

  import javaniochannels* //引入Javaniochannels包

  import javautil* //引入Javautil包

  public class TestServer implements Runnable

  {

  /**

  * 服務器Channel對象負責接受用戶連接

  */

  private ServerSocketChannel server

  /**

  * Selector對象負責監控所有的連接到服務器的網絡事件的發生

  */

  private Selector selector

  /**

  * 總的活動連接數

  */

  private int activeSockets

  /**

  * 服務器Channel綁定的端口號

  */

  private int port

  /**

  *

  * 構造函數

  */

  public TestServer()throws IOException

  {

  activeSockets=

  port=//初始化服務器Channel綁定的端口號為

  selector= Selectoropen()//初始化Selector對象

  server=ServerSocketChannelopen()//初始化服務器Channel對象

  ServerSocket socket=serversocket()//獲取服務器Channel對應的//ServerSocket對象

  socketbind(new InetSocketAddress(port))//把Socket綁定到監聽端口

  nfigureBlocking(false)//將服務器Channel設置為非阻塞模式

  serverregister(selectorSelectionKeyOP_ACCEPT)//將服務器Channel注冊到

  Selector對象並指出服務器Channel所感興趣的事件為可接受請求操作

  }

  public void run()

  {

  while(true)

  {

  try

  {

  /**

  *應用Select機制輪循是否有用戶感興趣的新的網絡事件發生當沒有

  * 新的網絡事件發生時此方法會阻塞直到有新的網絡事件發生為止

  */

  selectorselect()

  }

  catch(IOException e)

  {

  continue//當有異常發生時繼續進行循環操作

  }

  /**

  * 得到活動的網絡連接選擇鍵的集合

  */

  Set<SelectionKey> keys=selectorselectedKeys()

  activeSockets=keyssize()//獲取活動連接的數目

  if(activeSockets==)

  {

  continue//如果連接數為則繼續進行循環操作

  }

  /**

  /**

  * 應用For—Each循環遍歷整個選擇鍵集合

  */

  for(SelectionKey key :keys)

  {

  /**

  * 如果關鍵字狀態是為可接受則接受連接注冊通道以接受更多的*

  事件進行相關的服務器程序處理

  */

  if(keyisAcceptable())

  {

  doServerSocketEvent(key)

  continue

  }

  /**

  * 如果關鍵字狀態為可讀則說明Channel是一個客戶端的連接通道

  * 進行相應的讀取客戶端數據的操作

  */

  if(keyisReadable())

  {

  doClientReadEvent(key)

  continue

  }

  /**

  * 如果關鍵字狀態為可寫則也說明Channel是一個客戶端的連接通道

  * 進行相應的向客戶端寫數據的操作

  */

  if(keyisWritable())

  {

  doClinetWriteEvent(key)

  continue

  }

  }

  }

  }

  /**

  * 處理服務器事件操作

  * @param key 服務器選擇鍵對象

  */

  private void doServerSocketEvent(SelectionKey key)

  {

  SocketChannel client=null

  try

  {

  ServerSocketChannel server=(ServerSocketChannel)keychannel()

  client=serveraccept()

  if(client==null)

  {

  return

  }

  nfigureBlocking(false)//將客戶端Channel設置為非阻塞型

  /**

  /**

  * 將客戶端Channel注冊到Selector對象上並且指出客戶端Channel所感

  * 興趣的事件為可讀和可寫

  */

  clientregister(selectorSelectionKeyOP_READ|SelectionKeyOP_READ)

  }catch(IOException e)

  {

  try

  {

  clientclose()

  }catch(IOException e){}

  }

  }

  /**

  * 進行向客戶端寫數據操作

  * @param key 客戶端選擇鍵對象

  */

  private void doClinetWriteEvent(SelectionKey key)

  {

  代碼實現略

  }

  /**

  * 進行讀取客戶短數據操作

  * @param key 客戶端選擇鍵對象

  */

  private void doClientReadEvent(SelectionKey key)

  {

  代碼實現略

  }

  }

  從上面對代碼可以看出使用非阻塞性I/O進行並發型服務器程序設計分三個部分

  向Selector對象注冊感興趣的事件從Selector中獲取所感興趣的事件根據不同的事件進

  行相應的處理

  結語

  通過使用NIO 工具包進行並發型服務器程序設計一個或者很少幾個Socket 線程就可

  以處理成千上萬個活動的Socket 連接大大降低了服務器端程序的開銷同時網絡I/O 采取

  非阻塞模式線程不再在讀或寫時阻塞操作系統可以更流暢的讀寫數據並可以更有效地向

  CPU 傳遞數據進行處理以便更有效地提高系統的性能

  看到這裡相信你看了不止分鐘了吧  我說分鐘其實就是想讓大家能夠輕松的讀下去(雞蛋

  好了到這裡大家應該對java nio有個初步的了解了吧~~~

  下次給大家帶來一個java nio開發的實例吧~~


From:http://tw.wingwit.com/Article/program/Java/hx/201311/27190.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.