接着上面那篇JavaWeb有關的故事,講講NIO,上篇在這:JavaWeb有關的故事
幾個IO事實:html
進程執行IO操做,要麼把緩衝區的數據排幹(read),要麼用數據把緩衝區填滿(write)。
進程使用read()系統調用,要求其緩衝區須要被填滿,內核隨機向磁盤控制硬件發出命令,經過DMA(無需主CPU協助)講數據寫入內核內存緩衝區,一旦磁盤控制器把緩衝區裝滿,內核即把數據從內核空間的臨時緩衝區拷貝到進程read()調用時執行的緩衝區。java
其中用戶空間和內核空間
:
用戶空間是常規進程所在區域,如JVM。內核空間是操做系統所在區域,有特別權力,能與設備控制器通信,控制用戶區域進程的運行狀態等。總之,全部IO都直接或間接經過內核空間。
硬件不能直接訪問用戶空間,硬件設備操做的是固定大小的數據塊,而用戶進程請求的多是任意大小的或非對齊的數據塊。緩存
使用虛擬地址取代物理內存地址,有個兩大類好處,一是多個虛擬地址可知指向同一物理地址,二是虛擬內存空間可大於硬件內存。函數
文件系統與磁盤不一樣,文件系統是高層次的抽象,是安排、解釋磁盤或其餘隨機存取塊設備數據的一種獨特方法。
文件系統數據也會同其餘內存頁同樣獲得高速緩存,對於隨後發生的IO請求,文件數據的部分或所有可能仍位於物理內存中,無需從磁盤讀取。this
文件鎖定
:分爲共享和獨佔。多個共享鎖可同時對同一文件區域發生做用,獨佔鎖要求相關區域不能有其餘鎖起做用。操作系統
原理模仿通道,必須順序存取,如控制檯設備、打印機端口。code
Buffer類是nio構造基礎,一個Buffer對象是固定數量的數據容器,在這裏的數據可悲存儲並在以後用於檢索。能夠理解爲是I/O操做中數據的中轉站。緩衝區直接爲通道(Channel)服務,寫入數據到通道或從通道讀取數據,這樣的操利用緩衝區數據來傳遞就能夠達到對數據高效處理的目的。htm
容量capacity:建立時設定,不可改變。
上界limit:不可讀寫。現存元素的計數。
位置position:下一個要被讀或寫元素的索引。
標記mark:一個備忘位置。對象
java.nio.Buffer類是一個抽象類,不能被實例化。Buffer類的直接子類,如ByteBuffer等也是抽象類,因此也不能被實例化。
可是ByteBuffer類提供了靜態工廠方法來得到ByteBuffer的實例,終於到了show代碼的時候了=。=,以下:blog
//初始化 ByteBuffer buffer = ByteBuffer.allocate(1024); //數據填充 buffer.put((byte) 'H').put((byte) 'e').put((byte) 'l').put((byte) 'l').put((byte) 'o'); System.out.println(buffer.toString());//java.nio.HeapByteBuffer[pos=5 lim=1024 cap=1024] System.out.println((char) (buffer.get(0)));//H //數據修改 buffer.put(0, (byte) 'M').put((byte) 'w'); System.out.println(buffer.toString());//java.nio.HeapByteBuffer[pos=6 lim=1024 cap=1024] System.out.println((char) (buffer.get(0)));//M //flip將position歸0,將一個待填充狀態的緩衝區翻轉成準備讀出狀態 System.out.println(buffer);//java.nio.HeapByteBuffer[pos=6 lim=1024 cap=1024] buffer.flip(); //buffer.rewind();//相似flip,可是不影響limit屬性。java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024] System.out.println(buffer);//java.nio.HeapByteBuffer[pos=0 lim=6 cap=1024] //使用hasRemaining()判斷是否達到緩衝區上界 while (buffer.hasRemaining()) { System.out.print((char) (buffer.get())); }//Mellow System.out.println(""); //壓縮,compact丟棄已釋放數據,保留未釋放數據 buffer.compact(); //mark,在某個位置標記,reset( )函數將位置設爲當前的標記值。 // 若是標記值未定義,調 用 reset( )將致使 InvalidMarkException 異常。 // 一些緩衝區函數會拋棄已經設定的標記 (rewind( ),clear( ),以及 flip( )老是拋棄標記)。 buffer.position(2).mark();
比較兩個緩衝區,ByteBuffer已經實現Comparable接口,源碼以下:
public int compareTo(ByteBuffer that) { int n = this.position() + Math.min(this.remaining(), that.remaining()); for (int i = this.position(), j = that.position(); i < n; i++, j++) { int cmp = compare(this.get(i), that.get(j)); if (cmp != 0) return cmp; } return this.remaining() - that.remaining(); } private static int compare(byte x, byte y) { return Byte.compare(x, y); }
//批量取 byte[] myArray = new byte[1000]; buffer.get(myArray); //等價於: buffer.get(myArray, 0, myArray.length); //批量存 byte[] myString = new byte[1000]; buffer.put(myString); //等價於: buffer.put(myString,0,myString.length);
使用duplicate()函數能夠複製緩衝區,會建立一個新的buffer對象,但並不會複製數據,原始緩衝區和副本都會操做一樣的數據元素。
全部的基本數據類型都有相應的緩衝區類(布爾除外),字節類型比較特殊,字節是操做系統和其IO設備使用的基本類型。
非字節類型的基本類型,也是由字節組合成的,好比char2個字節,int4個字節,double8個字節。組合的字節是有順序的,Java默認的字節順序是大端字節順序,參見類ByteOreder。
字節緩衝區能夠成爲通道所執行IO的源頭或目標,非字節緩衝區傳遞給通道,會隱含執行復制內容到一個臨時ByteBuffer,因此直接緩衝區是IO的最佳選擇。
經過isDirect()函數,斷定緩衝區是否爲直接緩衝區。
視圖緩衝區,這種視圖對象維持它本身的屬性、容量、位置、上界和標記,可是和原來緩衝區共享數據元素。
CharBuffer charBuffer = byteBuffer.asCharBuffer( );
ByteBuffer類爲每一種原始數據類型提供了存取和轉化的方法,好比getInt()函數被調用,從當前位置開始的4個字節會被包裝成一個int類型變量返回。
對於無符號數據,自行實現,注意精度問題。
以上來自天團運營總監:坤少