JAVA NIO

NIO是非阻塞的IO,Java NIO由一下幾個核心部分組成:Buffers、Channels、Selectors。

Java NIO中有很多類和組件,但是Buffers、ChannelsSelectors構(gòu)成了核心的API。其他組件如PipeFileLock,只不過是與其他三個核心組件共同使用的工具類。

  • Channel:基本上所有的IO在NIO中都從一個Channel開始,Channel有點像流。數(shù)據(jù)可以從Channel讀到Buffer中,也可以從Buffer寫到Channel中。Channel和Buffer有好多類型,Channel主要有:FileChannel、DataGramChannel、SocketChannel、ServerSocketChannel。涵蓋了UDP和TCP網(wǎng)絡(luò)的IO以及文件IO。
  • Buffer:NIO主要的Buffer有:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer,這些Buffer涵蓋了你能通過IO發(fā)送的基本數(shù)據(jù)類型。
  • Selector:允許單線程處理多個Channel。如果你的應(yīng)用打開了多個連接(通道),但每個連接的流量都很低,使用Selector就會很方便。例如一個聊天服務(wù)器中。要使用Selector,得先向Selector注冊Channel然后調(diào)用它的select()方法。這個方法會一直堵塞知道某個注冊的通道有事件就緒。一旦這個方法返回線程就可以處理這些事件,事件的例子有如新連接進來,數(shù)據(jù)接收等。

緩沖區(qū)&Buffer

在整個Java新IO中,所有的操作都是以緩沖區(qū)進行的,使用緩沖區(qū),則操作的性能將是最高的。

在Buffer中存在一系列的狀態(tài)變量,隨著寫入或讀取都有可能被改變,在緩沖區(qū)可以使用三個值表示緩沖區(qū)狀態(tài)

  • position 位置
  • limit 界限
  • capacity 容量

ByteBuffer的使用

  1. 創(chuàng)建ByteBuffer

(1)使用allocate()靜態(tài)方法ByteBuffer buffer=ByteBuffer.allocate(256);
以上方法將創(chuàng)建一個容量為256字節(jié)的ByteBuffer,如果發(fā)現(xiàn)創(chuàng)建的緩沖區(qū)容量太小,唯一的選擇就是重新創(chuàng)建一個大小合適的緩沖區(qū).

(2)通過包裝一個已有的數(shù)組來創(chuàng)建
如下,通過包裝的方法創(chuàng)建的緩沖區(qū)保留了被包裝數(shù)組內(nèi)保存的數(shù)據(jù).
ByteBuffer buffer=ByteBuffer.wrap(byteArray);
如果要將一個字符串存入ByteBuffer,可以如下操作:
String sendString="你好,服務(wù)器. "; ByteBuffer sendBuffer=ByteBuffer.wrap(sendString.getBytes("UTF-16"));

  1. 緩沖區(qū)
  • buffer.flip();

這個方法用來將緩沖區(qū)準(zhǔn)備為數(shù)據(jù)傳出狀態(tài),執(zhí)行以上方法后,輸出通道會從數(shù)據(jù)的開頭而不是末尾開始.回繞保持緩沖區(qū)中的數(shù)據(jù)不變,只是準(zhǔn)備寫入而不是讀取.

  • buffer.clear();

這個方法實際上也不會改變緩沖區(qū)的數(shù)據(jù),而只是簡單的重置了緩沖區(qū)的主要索引值.不必為了每次讀寫都創(chuàng)建新的緩沖區(qū),那樣做會降低性能.相反,要重用現(xiàn)在的緩沖區(qū),在再次讀取之前要清除緩沖區(qū).

ByteBuffer轉(zhuǎn)為其他的Buffer,如:CharBuffer、DoubleBuffer、IntBuffer、LongBuffer、ShortBuffer,都有對應(yīng)的asXXXBuffer()方法。

  • 創(chuàng)建子緩沖區(qū) buf.slice()
    子緩沖區(qū)可以修改數(shù)據(jù)
import java.nio.IntBuffer ;
public class IntBufferDemo02{
 public static void main(String args[]){
   IntBuffer buf = IntBuffer.allocate(10) ;  // 準(zhǔn)備出10個大小的緩沖區(qū)
   IntBuffer sub = null ;  // 定義子緩沖區(qū)
   for(int i=0;i<10;i++){
     buf.put(2 * i + 1) ;  // 在主緩沖區(qū)中加入10個奇數(shù)
   }
   
   // 需要通過slice() 創(chuàng)建子緩沖區(qū)
   buf.position(2) ;
   buf.limit(6) ;
   sub = buf.slice() ;
   for(int i=0;i<sub.capacity();i++){
     int temp = sub.get(i) ;
     sub.put(temp-1) ;
   }

   buf.flip() ;  // 重設(shè)緩沖區(qū)
   buf.limit(buf.capacity()) ;
   System.out.print("主緩沖區(qū)中的內(nèi)容:") ;
   while(buf.hasRemaining()){
     int x = buf.get() ;
     System.out.print(x + "、") ;
   }
 }
}
  • 創(chuàng)建只讀緩沖區(qū) buf.asReadOnlyBuffer()
import java.nio.IntBuffer ;
public class IntBufferDemo03{
  public static void main(String args[]){
    IntBuffer buf = IntBuffer.allocate(10) ;  // 準(zhǔn)備出10個大小的緩沖區(qū)
    IntBuffer read = null ; // 定義子緩沖區(qū)
    for(int i=0;i<10;i++){
      buf.put(2 * i + 1) ;  // 在主緩沖區(qū)中加入10個奇數(shù)
    }
    read = buf.asReadOnlyBuffer()  ;// 創(chuàng)建只讀緩沖區(qū)
    
    read.flip() ; // 重設(shè)緩沖區(qū)
    System.out.print("主緩沖區(qū)中的內(nèi)容:") ;
    while(read.hasRemaining()){
      int x = read.get() ;
      System.out.print(x + "、") ;
    }
    read.put(30) ;  // 修改,錯誤
  }
}
  • 創(chuàng)建直接緩沖區(qū) public static ByteBuffer allocateDirect(int capacity)
    只能提高一些盡可能的性能。
import java.nio.ByteBuffer ;
public class ByteBufferDemo01{
  public static void main(String args[]){
    ByteBuffer buf = ByteBuffer.allocateDirect(10) ;  // 準(zhǔn)備出10個大小的緩沖區(qū)
    byte temp[] = {1,3,5,7,9} ; // 設(shè)置內(nèi)容
    buf.put(temp) ; // 設(shè)置一組內(nèi)容
    buf.flip() ;

    System.out.print("主緩沖區(qū)中的內(nèi)容:") ;
    while(buf.hasRemaining()){
      int x = buf.get() ;
      System.out.print(x + "、") ;
    }
  }
}

通道

  • 可讀、可寫。程序不會直接操作通道,所有的內(nèi)容讀到或者寫入緩沖區(qū),再通過緩沖區(qū)中取得或?qū)懭搿?/li>
  • 傳統(tǒng)的流操作分為輸入或輸出流,而通道本身是雙向操作的,可以完成輸入也可以完成輸出。

讀入方式

  • RandomAccessFile:較慢
  • FileInputStream:較慢
  • 緩存讀?。核俣容^快
  • 內(nèi)存映射MapByteBuffer:最快!

FileChannel的三種內(nèi)存映射模式

NO. 常量 類型 描述
1 public static final FileChannel.MapMode.READ_ONLY 常量 只讀映射模式
2 public static final FileChannel.MapMode.READ_WRITE 常量 讀取/寫入映射模式
3 public static final FileChannel.MapMode.PRIVATE 常量 專用(寫入時拷貝)映射模式
import java.nio.ByteBuffer ;
import java.nio.MappedByteBuffer ;
import java.nio.channels.FileChannel ;
import java.io.File ;
import java.io.FileOutputStream ;
import java.io.FileInputStream ;
public class FileChannelDemo03{
  public static void main(String args[]) throws Exception{
    File file = new File("d:" + File.separator + "mldn.txt") ;  
    FileInputStream input = null ;
    input = new FileInputStream(file) ;
    FileChannel fin = null ;  // 定義輸入的通道
    fin = input.getChannel() ;  // 得到輸入的通道
    MappedByteBuffer mbb = null ; 
    mbb = fin.map(FileChannel.MapMode.READ_ONLY,0,file.length()) ;
    byte data[] = new byte[(int)file.length()] ;  // 開辟空間接收內(nèi)容
    int foot = 0 ;
    while(mbb.hasRemaining()){
      data[foot++] = mbb.get() ;  // 讀取數(shù)據(jù)
    }
    System.out.println(new String(data)) ;  // 輸出內(nèi)容
    fin.close() ;
    input.close() ;
  }
}

內(nèi)存映射在讀取時速度快,但是如果在使用以上操作代碼的時候,執(zhí)行的是寫入操作則有可能非常危險,因為僅僅是改變數(shù)組中單個元素這種簡單的操作,就可能直接修改磁盤上的文件,因為修改數(shù)據(jù)與將數(shù)據(jù)保存在磁盤上是一樣的。


文件鎖FileLock

當(dāng)一個線程將文件鎖定之后,其它線程無法操作此文件,要想進行文件鎖定操作,需要使用FileLock類完成,此類的對象需要依靠FileChannel進行實例化操作。

實例化`FileLock`對象的方法

鎖定方式

  • 共享鎖:允許多個線程進行文件的讀/寫操作

  • 獨占鎖:只允許一個線程進行文件的讀/寫操作


字符集 Charset

整個NIO中,對于不同平臺的編碼操作,java都可以進行自動適應(yīng),因為可以使用字符集進行字符編碼的轉(zhuǎn)換。

在java中,所有信息都是以UNICODE進行編碼,計算機中存在多種編碼,NIO中提供了charset類來處理編碼問題,包括了創(chuàng)建編碼器(CharsetEndoder)和創(chuàng)建解碼器(CharsetDecoder)的操作。

得到所有字符集
        Charset latin1 = Charset.forName("ISO-8859-1") ;  // 只能表示的英文字符
    CharsetEncoder encoder = latin1.newEncoder() ;  // 得到編碼器
    CharsetDecoder decoder = latin1.newDecoder() ;  // 得到解碼器
    // 通過CharBuffer類中的
    CharBuffer cb = CharBuffer.wrap("啊哈哈哈哈") ;
    ByteBuffer buf = encoder.encode(cb) ; // 進行編碼操作
    System.out.println(decoder.decode(buf)) ; // 進行解碼操作

Selector

解決服務(wù)器端的通訊性能。

  • 使用Selector可以構(gòu)建一個非阻塞的網(wǎng)絡(luò)服務(wù)
  • 在NIO中實現(xiàn)網(wǎng)絡(luò)程序需要依靠ServerSocketChannel類與SocketChannel
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.Set;
import java.util.Iterator;
import java.util.Date;
import java.nio.channels.ServerSocketChannel;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;

public class DateServer {
  public static void main(String args[]) throws Exception {
    int ports[] = { 8000, 8001, 8002, 8003, 8005, 8006 }; // 表示五個監(jiān)聽端口
    Selector selector = Selector.open(); // 通過open()方法找到Selector
    for (int i = 0; i < ports.length; i++) {
      ServerSocketChannel initSer = null;
      initSer = ServerSocketChannel.open(); // 打開服務(wù)器的通道
      initSer.configureBlocking(false); // 服務(wù)器配置為非阻塞
      ServerSocket initSock = initSer.socket();
      InetSocketAddress address = null;
      address = new InetSocketAddress(ports[i]); // 實例化綁定地址
      initSock.bind(address); // 進行服務(wù)的綁定
      initSer.register(selector, SelectionKey.OP_ACCEPT); // 等待連接
      System.out.println("服務(wù)器運行,在" + ports[i] + "端口監(jiān)聽。");
    }
    // 要接收全部生成的key,并通過連接進行判斷是否獲取客戶端的輸出
    int keysAdd = 0;
    while ((keysAdd = selector.select()) > 0) { // 選擇一組鍵,并且相應(yīng)的通道已經(jīng)準(zhǔn)備就緒
      Set<SelectionKey> selectedKeys = selector.selectedKeys();// 取出全部生成的key
      Iterator<SelectionKey> iter = selectedKeys.iterator();
      while (iter.hasNext()) {
        SelectionKey key = iter.next(); // 取出每一個key
        if (key.isAcceptable()) {
          ServerSocketChannel server = (ServerSocketChannel) key.channel();
          SocketChannel client = server.accept(); // 接收新連接
          client.configureBlocking(false);// 配置為非阻塞
          ByteBuffer outBuf = ByteBuffer.allocateDirect(1024); //
          outBuf.put(("當(dāng)前的時間為:" + new Date()).getBytes()); // 向緩沖區(qū)中設(shè)置內(nèi)容
          outBuf.flip();
          client.write(outBuf); // 輸出內(nèi)容
          client.close(); // 關(guān)閉
        }
      }
      selectedKeys.clear(); // 清楚全部的key
    }

  }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容