Java NIO 特性學習

Java NIO 特性學習

Java NIO 包含幾個核心的組件:
- Channels
- Buffer
- Selectors

Channels

這裏寫圖片描述

可以理解爲資源的一個流,通過這個流資源可以從Channel讀取Data到一個Buffer中或者從一個Buffer中寫入Data到Channel;

Channel Implementations

集中Jdk7常用的Channel上線
- FileChannel : 操作文件讀取或者寫入數據
- DatagramChannel : 從一個網絡UDP連接中讀取或寫入數據
- SocketChannel : 從一個TCP網絡連接中讀取或寫入數據
- ServerSocketChannel: 可以使用這個Channel來監聽TCP連接,如同Web Server 當連接來時可以創建出一個SocketChannel

Base Channel Example

Read Data from Channel

    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");
    FileChannel inChannel = aFile.getChannel();

    ByteBuffer buf = ByteBuffer.allocate(48);

    //write data to buffer
    int bytesRead = inChannel.read(buf);
    while (bytesRead != -1) {

      System.out.println("Read " + bytesRead);
      //swither buffer type to read data from buffer
      buf.flip();

      while(buf.hasRemaining()){
          System.out.print((char) buf.get());
      }

      buf.clear();
      bytesRead = inChannel.read(buf);
    }
    aFile.close();

Buffer

Java NIO Buffers 和Channels配合使用,Read from channels into buffer ,writtern from buffers into channels

Buffer Usage

  1. 向buffer中寫入數據
  2. 調用buffer.flip()方法
  3. 使用buffer.get()方法獲取出數據
  4. 調用buffer.clear() 或者 buffer.compact()清理已經讀取的數據
    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt" , "rw");
    FileChannel inChannel = aFile.getChannel();
    //Create Buffer with capacity of 48 byte
    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = inChannel.read(buf);

    while(bytesRead!=-1){

      buf.flip();  //make buffer ready for read

          while(buf.hasRemaining()){
              System.out.print((char) buf.get()); // read 1 byte at a time
          }

      buf.clear(); //make buffer ready for writing
      bytesRead = inChannel.read(buf);
    }

Buffer Capacity , Position and Limit

這裏寫圖片描述

Capacity

Buffer在創建的時候會分配規定大小的內存塊,並且在buffer寫入數據時只能寫入這個固定大小的數據。如果Buffer已經放滿數據,就需要先read 或者 clear 只能才能繼續寫入。

Position

Position 在讀模式和模式下面含義不一樣。Bufer init之後Position值爲0,當數據寫入one byte ,long etc Buffer Position相應的移動到下一個位置。並且Position<=Capacity-1

當調用filp()從Buffer讀取數據時會set position = 0,position會隨着讀取過程向下移動;

Limit

在Write模式下,Limit等於Capacity的值,表示能寫多少數據到Buffer中。

調用filp()進入Reade模式會set limit = position ,表示最多能讀的數據量。

常用的Buffer 實現

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

SomeMethods of Buffer

Allocating a Buffer

通過使用allocate() 來分配Buffer大小

ByteBuffer buf = ByteBuffer.allocate(48);
CharBuffer buf = CharBuffer.allocate(1024);

Write Data to a Buffer

向Buffer中寫入數據有兩種方法:
- 從Channel中讀取數據寫入Buffer
- 調用Buffer put() 方法寫入數據

int bytesRead = inChannel.read(buf); //read into buffer.
buf.put(127);

flip()

轉換寫狀態到讀狀態,發生動作:set limit = position ; set position=0 ;

Reading Data from a Buffer

兩種方法:
- 從Buffer中讀取數據到Channel
- 讀取Buffer中數據給自己,可以使用buffer自帶方法 get() etc

//read from buffer into channel.
int bytesWritten = inChannel.write(buf);
byte aByte = buf.get();

rewind()

set position=0

mark() 和 reset()

mark用於打點記錄position爲止,之後使用reset方法可以將position重置到mark記錄的位置。

Scatter / Gather /Transfers

Channel 可以執行讀取操作將數據讀取到多個Buffer中。
Channel 可以執行寫操作將多個Buffer的數據寫入Channle中。
Channel 可以在Channle之間做轉換包括transferFrom() & transferTo

Scatter Reads :

這裏寫圖片描述

例如我將html的header部分讀取到第一個buffer中,將body部分讀取到第二個Buffer中

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

ByteBuffer[] bufferArray = { header, body };

channel.read(buffers);

Gather Writes

這裏寫圖片描述

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);

//write data into buffers

ByteBuffer[] bufferArray = { header, body };

channel.write(buffers);

Transfers

TransferFrom()

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel      fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel      toChannel = toFile.getChannel();

long position = 0;
long count    = fromChannel.size();

toChannel.transferFrom(fromChannel, position, count);

TransferTo()

RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw");
FileChannel      fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw");
FileChannel      toChannel = toFile.getChannel();

long position = 0;
long count    = fromChannel.size();

fromChannel.transferTo(position, count, toChannel);

Selector

Selector組件可以運行判斷多個Channel,動態決定使用哪個Channel來執行Read 或者 Write操作。通過這個組件可以上線一個Thread 管理多個Channels 或者 多個網絡連接Channel。
這裏寫圖片描述

Create a Selector

通過使用Selector.open()方法來創建一個Selector

Selector selector = Selector.open();

註冊Channels到Selector上

爲了能讓Selector管理Channels需要調用Selector.register()註冊到Selector

channel.configureBlocking(false); //The Channel must be in non-blocking mode to be used with a Selector
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
註冊Channel的時候需要制定Channel需要關心的事件,事件包括:
- Connect -> SelectionKey.OP_CONNECT
- Accept -> SelectionKey.OP_ACCEPT
- Read -> SelectionKey.OP_READ
- Write ->SelectionKey.OP_WRITE
如果註冊多個事件可以使用 int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; 通過int interestSet = selectionKey.interestOps(); 可以得到所有Selector’s的事件。

Simple Demo:

Selector selector = Selector.open();

channel.configureBlocking(false);

SelectionKey key = channel.register(selector, SelectionKey.OP_READ);


while(true) {

  int readyChannels = selector.select();

  if(readyChannels == 0) continue;


  Set<SelectionKey> selectedKeys = selector.selectedKeys();

  Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

  while(keyIterator.hasNext()) {

    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()) {
        // a connection was accepted by a ServerSocketChannel.

    } else if (key.isConnectable()) {
        // a connection was established with a remote server.

    } else if (key.isReadable()) {
        // a channel is ready for reading

    } else if (key.isWritable()) {
        // a channel is ready for writing
    }

    keyIterator.remove();
  }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章