I/O問題能夠說是當今web應用中所面臨的的主要問題之一,大部分的web應用系統的瓶頸都是I/O瓶頸。這個系列主要介紹JAVA的I/O類庫基本架構、磁盤I/O工做機制、網絡I/O工做機制以及NIO的工做方式。java
IO模式 | BIO | NIO |
方式 | 從磁盤到磁盤 | 從緩存到磁盤 |
通訊 | 流 | 緩存(多級複用) |
處理 | 阻塞 | 非阻塞(Reactor) |
觸發 | 無 | 選擇器輪詢機制 |
從1.4版本開始JAVA引入了NIO,用來提高I/O性能。I/O操做類在包java.io下,大概有將近80個類web
這些類能夠分爲以下四組:數組
基於字節操做的I/O接口:InputStream和OutputStream緩存
基於字符操做的I/O接口:Reader和Writer網絡
基於磁盤操做的I/O接口:File架構
基於網絡操做的I/O接口:Socket異步
前兩組是傳輸數據的格式,後兩組是傳輸數據的方式socket
核心組件 | 定義 | 做用 | 特色 | 使用 |
通道性能 (Channel)大數據 |
NIO數據的源頭/目的地 (Buffer的惟一接口) |
向緩存提供數據 讀取換區的數據 |
雙向讀寫,異步讀寫 數據來源/流向 老是Buffer |
根據來源區別: FileChannel:從文件 DataChannel:從UDP網絡數據 SocketChannel: ServerSocketChannel |
緩存 (Buffer) |
NIO數據讀/寫中轉地 (一塊完整的內存塊) |
數據緩存 | 適用於處布爾外基本數據類型 | 7種基本數據類型
|
選擇器 (Selector) |
異步IO核心類 | 實現異步非阻塞IO | 使用一個Selector線程檢測1個/多個 通道channel上的事件,給予事件驅動 分發不須要爲每一個channel去分配一個線程 |
一、建立Selector對象 二、箱Slector註冊通道Channel 二、調用Selector類中select()方法 |
1) Buffer介紹: 緩衝區,本質就是一個數組,可是它是特殊的數組,緩衝區對象內置了一些機制,可以追蹤和記錄緩衝區的狀態變化狀況,若是咱們使用get方法從 緩衝區中獲取數據或者用put方法吧數據寫入緩衝區,都會引發緩衝區的狀態變化
在緩衝區中,最重要的屬性是以下三個,他們一塊兒合做完成了對緩衝區內容狀態的變化跟蹤
1)position:指定了下一個將要被寫入或者讀取的元素索引,它的值由get()/put() 方法自動更新,在新建立一個Buffer對象時,position被初始化爲0
2)limit:操做緩衝區的可操做空間和可操做範圍,指定還有多少數據須要去除,或者還有多少空間能夠放入數據
3)capacity:指定了能夠存儲在緩衝區中的最大數據容量,實際上,它指定了底層數組的大小,或者至少是指定了准許咱們使用的底層數組的容量。
以上三個屬性值之間有一些相對的大小的關係:0<=position<=limit<=capacity
package com.Allen.buffer; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class testBufferDemo01 { public static void main(String[] args) throws IOException { String fileURL="F://a.txt"; FileInputStream fis=new FileInputStream(fileURL); //獲取通路 FileChannel channel=fis.getChannel(); //定義緩衝區大小 ByteBuffer buffer=ByteBuffer.allocate(10); output("init", buffer); //先讀 channel.read(buffer); output("read", buffer); buffer.flip(); output("flip", buffer); while (buffer.hasRemaining()) { byte b=buffer.get(); } output("get", buffer); buffer.clear(); output("clear", buffer); fis.close(); } public static void output(String string,ByteBuffer buffer){ System.out.println(string); System.out.println(buffer.capacity()+":"+buffer.position()+":"+buffer.limit()); } }
任什麼時候候讀取數據,都不是直接從通道中讀取,而是從通道讀取到緩衝區,因此使用NIO讀取數據能夠分紅下面三個步驟
1)從FileInputStream獲取Channel
2)建立Buffer
3)將數據從Channel 讀取到Buffer中
package com.allen.test; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class testNio { public static void main(String[] args) throws IOException { String oldFileUrl="E://1.txt"; String newFileUrl="E://2.txt"; FileInputStream fis=new FileInputStream(oldFileUrl);
//一、獲取Channel FileChannel inChannel=fis.getChannel();
//二、建立Buffer ByteBuffer bf=ByteBuffer.allocate(1024); FileOutputStream fos=new FileOutputStream(newFileUrl); FileChannel outChannel=fos.getChannel(); while(true){ int eof=inChannel.read(bf); if(eof==-1){ break; }else{ bf.flip();
//三、把數據寫入緩存 outChannel.write(bf); bf.clear(); } } inChannel.close(); fis.close(); outChannel.close(); fos.close(); } }
// 1. 建立Selector對象 Selector sel = Selector.open(); // 2. 向Selector對象綁定通道 // a. 建立可選擇通道,並配置爲非阻塞模式 ServerSocketChannel server = ServerSocketChannel.open(); server.configureBlocking(false); // b. 綁定通道到指定端口 ServerSocket socket = server.socket(); InetSocketAddress address = new InetSocketAddress(port); socket.bind(address); // c. 向Selector中註冊感興趣的事件 server.register(sel, SelectionKey.OP_ACCEPT); return sel; // 3. 處理事件 try { while(true) { // 該調用會阻塞,直到至少有一個事件就緒、準備發生 selector.select(); // 一旦上述方法返回,線程就能夠處理這些事件 Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> iter = keys.iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); process(key); } } } catch (IOException e) { e.printStackTrace(); }