Java NIO概述

  • 2020 年 3 月 17 日
  • 筆記

傳統的輸入輸出流都是阻塞的輸入輸出。舉個列子:當用傳統的流進行數據輸入時,如果流中沒有數據,它會阻塞當前執行緒往下執行,等到從流中讀到數據為止。另外傳統的輸入輸出流每次處理的是一個位元組或一個字元,通常效率不是很高。從JDK 1.4開始 Java提供了NIO功能,可以代替傳統的輸入輸出功能,在效率上也有很大提升。

標準的IO基於位元組流和字元流進行操作的,而NIO是基於通道(Channel)和緩衝區(Buffer)進行操作,數據總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中(雙向操作)。NIO可以使用非阻塞模式。

NIO概述

NIO在處理文件時會將文件的一段區域直接映射到記憶體中,這樣訪問文件時就可以像訪問記憶體一樣,比傳統的輸入輸出要快很多。主要的實現類都在java.nio下面。

Channel和Buffer是NIO中兩個核心的概念。Channel的概念和傳統的InputStram和OutputStream對標,最大的區別是Channel提供了一個map()方法將文件的塊數據映射到記憶體中。可以面向一大塊數據進行處理。Buffer可以理解成緩衝,其本質是一個數組。從Channel中讀出來的數據要先存在Buffer中,要寫到Channel中的數據也要先放到Buffer中。

另外,NIO還提供了將Unicode字元串映射成位元組序列的Charset類,以及支援非阻塞輸入輸出的Selector類。

Channels and Buffers

標準的IO基於位元組流和字元流進行操作的,而NIO是基於通道(Channel)和緩衝區(Buffer)進行操作,數據總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。Channel、Buffer和Selectors是NIO的核心組件。

Channel常用的實現類:

  • FileChannel:文件
  • DatagramChannel:UDP數據報
  • SocketChannel:TCP客戶端
  • ServerSocketChannel:TCP服務端

Buffer常見實現類:

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

Buffer的使用

Buffer的本質就是一個緩衝區,但是Buffer提供了豐富的API來讓我們操作這塊數據區。

System.out.println("capacity:"+buffer.capacity());  System.out.println("limit:"+buffer.limit());  System.out.println("length:"+buffer.length());  System.out.println("position:"+buffer.position());    buffer.append("a");  buffer.append('b');  buffer.put('c');  System.out.println("---------------------------");    System.out.println("capacity:"+buffer.capacity());  System.out.println("limit:" + buffer.limit());  System.out.println("length:" + buffer.length());  System.out.println("position:" + buffer.position());    //flip方法會將limit的位置移動到當前posiion位置,這樣Buffer中沒  //賦值的空間將都不能被訪問。通常flip方法是為讀取數據做準備的,可以  //防止讀到null數據,讀取完畢之後調用clear方法  buffer.flip();  System.out.println("---------------------------");    System.out.println("capacity:"+buffer.capacity());  System.out.println("limit:" + buffer.limit());  System.out.println("length:" + buffer.length());  System.out.println("position:" + buffer.position());

Channel的使用

FileInputStream fis = new FileInputStream("file.txt");  FileChannel channel = fis.getChannel();    ByteBuffer buffer = ByteBuffer.allocate(1024);  int hasRead = 0;  while ((hasRead=channel.read(buffer))>0){  byte[] buff = new byte[1024];  buffer.flip();  buffer.get(buff, 0, hasRead);  System.out.println(new String(buff,0,hasRead));  buffer.clear();  }

Selector

Selector(選擇器)是Java NIO中能夠檢測一到多個NIO通道,並能夠知曉通道是否為諸如讀寫事件做好準備的組件。這樣,一個單獨的執行緒可以管理多個channel,從而管理多個網路連接。與Selector一起使用時,Channel必須處於非阻塞模式下。這意味著不能將FileChannel與Selector一起使用,因為FileChannel不能切換到非阻塞模式。而套接字通道都可以

Selector selector = Selector.open();  channel.configureBlocking(false);  //註冊到Selector上  SelectionKey key = channel.register(selector, SelectionKey.OP_READ);  while(true) {      int readyChannels = selector.select();      if(readyChannels == 0)          continue;      Set selectedKeys = selector.selectedKeys();      Iterator 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();     }  }

CharSet

用於對字元串編解碼

JDK7的NIO2

JDK 1.7版本對NIO進行優化改進。Path、Paths和Files這些類、Filevisiter、watchService AsynchronousFileChannel這些類進行文件內容的非同步讀寫。AsynchronousSocketChannel這些類進行伺服器IO非同步讀寫。

BIO、NIO和AIO的區別

  • BIO (Blocking I/O):同步阻塞I/O模式,數據的讀取寫入必須阻塞在一個執行緒內等待其完成。這裡使用那個經典的燒開水例子,這裡假設一個燒開水的場景,有一排水壺在燒開水,BIO的工作模式就是, 叫一個執行緒停留在一個水壺那,直到這個水壺燒開,才去處理下一個水壺。但是實際上執行緒在等待水壺燒開的時間段什麼都沒有做。(特點就是執行緒必須等待數據讀取或者寫入完成才能繼續干其他事情。

  • NIO (New I/O):同時支援阻塞與非阻塞模式(文件channel只支援阻塞模式,socket的channel支援阻塞和非阻塞模式),但這裡我們以其同步非阻塞I/O模式來說明,那麼什麼叫做同步非阻塞?如果還拿燒開水來說,NIO的做法是叫一個執行緒不斷的輪詢每個水壺的狀態,看看是否有水壺的狀態發生了改變,從而進行下一步的操作。(特點就是執行緒不必等待IO讀寫完成,在IO進行過程中執行緒可以不停地輪詢IO的狀態,一旦發現IO狀態變化,就可以做出相應處理

  • AIO ( Asynchronous I/O):非同步非阻塞I/O模型。非同步非阻塞與同步非阻塞的區別在哪裡?非同步非阻塞無需一個執行緒去輪詢所有IO操作的狀態改變,在相應的狀態改變後,系統會通知對應的執行緒來處理。對應到燒開水中就是,為每個水壺上面裝了一個開關,水燒開之後,水壺會自動通知我水燒開了。AIO中雖然不需要執行緒來輪詢,但是需要執行緒來等待通知。

    另外,AIO的非同步特性並不是Java實現的,而是使用了系統底層API的支援,在Unix系統下,採用了epoll IO模型,而windows便是使用了IOCP模型

參考