NIO相關基礎篇一

匠心零度發表於2017-12-11

轉載請註明原創出處,謝謝!

說在前面

NIO相關知識是很多後續的一些基礎知識,所以今天這篇文章僅僅是簡單介紹,後續會繼續有一到二篇相關NIO內容。

什麼是NIO

Java NIO( New IO) 是從Java 1.4版本開始引入的一個新的IO API,可以替代標準的Java IO API。NIO與原來的IO有同樣的作用和目的,但是使用的方式完全不同, NIO支援面向緩衝區的、基於通道的IO操作。 NIO將以更加高效的方式進行檔案的讀寫操作。

NIO與普通IO的主要區別

IO NIO
面向流(Stream Oriented) 面向緩衝區(Buffer Oriented)
阻塞IO(Blocking IO) 非阻塞IO(Non Blocking IO)
(無) 選擇器(Selectors)
  • Channels and Buffers(通道和緩衝區):標準的IO基於位元組流和字元流進行操作的,而NIO是基於通道(Channel)和緩衝區(Buffer)進行操作,資料總是從通道讀取到緩衝區中,或者從緩衝區寫入到通道中。
  • Asynchronous IO(非同步IO):Java NIO可以讓你非同步的使用IO,例如:當執行緒從通道讀取資料到緩衝區時,執行緒還是可以進行其他事情。當資料被寫入到緩衝區時,執行緒可以繼續處理它。從緩衝區寫入通道也類似。
  • Selectors(選擇器):Java NIO引入了選擇器的概念,選擇器用於監聽多個通道的事件(比如:連線開啟,資料到達)。因此,單個的執行緒可以監聽多個資料通道。

**說明:**Asynchronous IO(非同步IO)、Selectors(選擇器)等後續文章會繼續介紹的。

緩衝區(Buffer)

通過上面NIO與普通IO的主要區別也可以看到在基本的IO操作中所有的操作都是基於流進行操作的,而在NIO中所有的操作都是基於緩衝區繼續操作的,所有的讀寫操作都是通過快取區來進行完成,緩衝區(Buffer)是一個線性的、有序的資料集,只能容納特定的資料型別(基本就是基本資料型別對應的Buffer或者起子類)。

各各資料型別的快取區類

快取區類 相關描述
ByteBuffer 儲存位元組的Buffer
CharBuffer 儲存字元的Buffer
ShortBuffer 儲存短整型的Buffer
IntBuffer 儲存整型的Buffer
LongBuffer 儲存長整型的Buffer
FloatBuffer 儲存單精度浮點型Buffer
DoubleBuffer 儲存雙精度浮點型Buffer

**備註:**看到上面這幾類是不是想起了JAVA的8種基本資料型別,唯一缺少boolean對於的型別。

第一問:為什麼boolean不需要快取呢? 可以查閱之前寫的:java二進位制相關基礎,裡面有描述規範中數字的內部表示和儲存,boolean所佔位數1bit(取值只有true或者false),由於位元組(byte)是作業系統和所有I/O裝置使用的基本資料型別,所以基本都是以位元組或者連續的一段位元組來儲存表示,所以就沒有boolean,感覺也沒有必要boolean型別的快取操作(像RocketMQ原始碼裡面可能把一個Int裡面的某位來表示boolean,其他位繼續來儲存資料,歡迎關注我的公眾號【匠心零度】,後續RocketMQ原始碼類分析的時候如何運用上述技巧進行說明等,其實上面我寫的好幾篇文章都是為了後續RocketMQ原始碼分析做準備的)。

Buffer使用

讀資料:

  • flip()方法
    • 將Buffer從寫模式切換到讀模式
    • 呼叫flip()方法會將position設回0,並將limit設定成之前position的值。
    • buf.flip();
  • buf.get()
    • 讀取資料
  • Buffer.rewind()
    • 將position設回0,所以你可以重讀Buffer中的所有資料
    • limit保持不變,仍然表示能從Buffer中讀取多少個元素(byte、char等)
  • Buffer.mark()方法,可以標記Buffer中的一個特定position。之後可以通過呼叫。
  • Buffer.reset()方法,恢復到Buffer.mark()標記時的position。
  • clear()方法會:
    • 清空整個緩衝區。
    • position將被設回0,limit被設定成 capacity的值
  • compact()方法:
    • 只會清除已經讀過的資料;任何未讀的資料都被移到緩衝區的起始處,新寫入的資料將放到緩衝區未讀資料的後面。
    • 將position設到最後一個未讀元素正後面,limit被設定成 capacity的值。

寫資料: buf.put(127);

緩衝區的基本屬性

  • 容量 (capacity):表示 Buffer 最大資料容量,緩衝區容量不能為負,並且建立後不能更改。
  • 限制 (limit):第一個不應該讀取或寫入的資料的索引,即位於 limit 後的資料不可讀寫。緩衝區的限制不能為負,並且不能大於其容量。
  • 位置 (position):下一個要讀取或寫入的資料的索引。緩衝區的位置不能為負,並且不能大於其限制。

**備註:**標記、 位置、 限制、 容量遵守以下不變式: 0 <= position <= limit <= capacity。

為了更形象解釋上面重要屬性,準備配上簡單程式碼以及圖來進行說明就容易懂了。

//第一步,獲取IntBuffer,通過IntBuffer.allocate操作
IntBuffer buf = IntBuffer.allocate(10) ;	// 準備出10個大小的緩衝區

//第二步未操作前輸出屬性值
System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;

//第三步進行設定資料
buf.put(6) ;	// 設定一個資料
buf.put(16) ;	// 設定二個資料

//第四步操作後輸出屬性值
System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;

//第五步將Buffer從寫模式切換到讀模式 postion = 0 ,limit = 原本position
buf.flip() ;	 

//第六步操作後輸出屬性值
System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;
複製程式碼

程式輸出結果:

position = 0,limit = 10,capacty = 10
position = 2,limit = 10,capacty = 10
position = 0,limit = 2,capacty = 10
複製程式碼

檢視下圖來進行說明:

NIO相關基礎篇一

通道(Channel)

通道表示開啟到 IO 裝置(例如:檔案、套接字)的連線。若需要使用 NIO 系統,需要獲取用於連線 IO 裝置的通道以及用於容納資料的緩衝區。然後操作緩衝區,對資料進行處理。Channel 負責傳輸, Buffer 負責儲存。通道是由 java.nio.channels 包定義的。 Channel 表示 IO 源與目標開啟的連線。Channel 類似於傳統的“流”。只不過 Channel本身不能直接訪問資料, Channel 只能與Buffer 進行互動。

通道都是操作快取區完成全部的功能的。

Java中所有已知 Channel 實現類:

  • AbstractInterruptibleChannel
  • AbstractSelectableChannel
  • DatagramChannel
  • FileChannel
  • Pipe.SinkChannel
  • Pipe.SourceChannel
  • SelectableChannel
  • ServerSocketChannel
  • SocketChannel

常用的有入下幾個:

  • FileChannel:用於讀取、寫入、對映和操作檔案的通道。
  • DatagramChannel:通過 UDP 讀寫網路中的資料通道。
  • SocketChannel:通過 TCP 讀寫網路中的資料。
  • ServerSocketChannel:可以監聽新進來的 TCP 連線,對每一個新進來的連線都會建立一個 SocketChannel。

獲取通道

獲取通道的一種方式是對支援通道的物件呼叫getChannel() 方法。支援通道的類如下:

  • FileInputStream
  • FileOutputStream
  • RandomAccessFile
  • DatagramSocket
  • Socket
  • ServerSocket

獲取通道的其他方式是使用 Files 類的靜態方法 newByteChannel() 獲取位元組通道。或者通過通道的靜態方法 open() 開啟並返回指定通道。

FileChannel

  • 為了更形象解釋說明的Channel,下面準備以FileChannel的一些簡單程式碼進行說明就容易懂了。
  • 準備以FileOutputStream類為準,這兩個類都是支援通道操作的。
String info[] = {"歡迎","關注","匠心零度","的","公眾號","謝謝!!"} ;
File file = new File("d:" + File.separator + "testfilechannel.txt") ; 
FileOutputStream output = null ;
FileChannel fout = null;
try {
	output = new FileOutputStream(file) ;
	fout = null;
	fout = output.getChannel() ;	// 得到輸出的通道
	ByteBuffer buf = ByteBuffer.allocate(1024) ; 
	for(int i=0;i<info.length;i++){
		buf.put(info[i].getBytes()) ;	// 字串變為位元組陣列放進緩衝區之中
	}
	buf.flip() ;
	fout.write(buf) ;	// 輸出緩衝區的內容
} catch (Exception e) {
	e.printStackTrace();
}finally{
	if(fout!=null){
		try {
			fout.close() ;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	if(output!=null){
		try {
			output.close() ;
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
複製程式碼

程式執行效果:

NIO相關基礎篇一

**說明:**今天只是NIO相關基礎篇一,所以有很多並沒有涉及到,希望上面說的這樣讓大家有一個新的瞭解,未完待續……

如果讀完覺得有收穫的話,歡迎點贊、關注、加公眾號【匠心零度】。


個人公眾號,歡迎關注,查閱更多精彩歷史!!!

匠心零度公眾號

相關文章