一、IO與NIO的區別:
前提我們先說一說java IO:
Java中使用IO(輸入輸出)來讀取和寫入,讀寫設備上的數據、硬盤文件、內存、鍵盤......,根據數據的走向可分為輸入流和輸出流,這個走向是以內存為基准的,即往內存中讀數據是輸入流,從內存中往外寫是輸出流。
根據處理的數據類型可分為字節流和字符流:
1.字節流可以處理所有數據類型的數據,在java中以Stream結尾
2.字符流處理文本數據,在java中以Reader和Writer結尾。
我們來看個IO流的詳解圖:
1.NIO的起源:
java.NIO是jdk1.4是提出,jdk1.7進行二次改進的java新型IO類,其最大的特點就是它的非阻塞性。
2.IO與NIO結構差異:
普通的IO是面向流(Stream Oriented),而NIO則是面向緩沖區(Buffer Oriented)。IO流是單向的,直接面向字節流,通過InputStream、OutputStream來完成數據的輸入輸出。而NIO是雙向的,通過建立通道(Channel),然后將數據裝在緩沖區(Buffer)在通道上進行傳輸。針對不同類型的數據有不同的Buffer,根據數據類型不同(boolean 除外),提供了相應類型的緩沖區: ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、 FloatBuffer、DoubleBuffer。
二、NIO的Buffer的數據存取:
1.創建Buffer實例:
ByteBuffer buf = ByteBuffer.allocate(1024); //1024為capacity,通過allocate()方法可以獲取一個緩沖區
2.Buffer類屬性的解析:
Buffer類中有三個屬性必須理解:capacity(容量)、limit(訪問范圍)、position(位置,表示緩沖區中正在操作數據的位置)。通過get(),put()方法進行數據的存取。
通過flip()方法切換成讀模式,clear()方法清空緩沖區。但是緩沖區中的數據依然存在,但是處於“被遺忘”狀態,rewind()可重復讀。
3.Buffer的分類:
Buffer分為直接緩沖區和非直接緩沖區。非直接緩沖區 通過allocate()方法分配緩沖區,將緩沖區建立在JVM的內存中。直接緩沖區 通過allocateDirect()方法,將緩沖區建立在物理內存中。這樣做可以提高IO效率,節省了copy的過程,直接緩沖區是物理內存映射文件,但是寫入過程不受控制,讀過程受GC影響!
三、通道的原理與獲取:
1.通道的原理:
傳統的javaIO是通過DMA的方式存取,這種方式需要CPU的權限。而通道(Channel)自帶處理器,不需要去訪問CPU,所以在進行大量IO時效率更高一些。通道用於源節點與目標節點的連接。在 Java NIO 中負責緩沖區中數據的傳輸。Channel 本身不存儲數據,因此需要配合緩沖區進行傳輸。
2.通道的獲取:
通道的主要實現類 java.nio.channels.Channel 接口:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel。
獲取通道的方法:
1)各IO有自己的獲取方法、
2)jdk1.7的NIO2,針對各個通道提供了靜態方法open();
3)jdk1.7的NIO2的Files工具類的newByteChannel();
四、通道的數據傳輸:
1.分散(Scatter)與聚集(Gather):
1)分散讀取(Scattering Reads),將Channel中讀取的數據分散到Buffer
2)聚集寫入(Gathering Writes),將多個Buffer中的數據聚集到Channel中
2.實例代碼:
RandomAccessFile raf1 = new RandomAccessFile("1.txt", "rw"); //1. 獲取通道 FileChannel channel = raf1.getChannel(); //2. 分配指定大小的緩沖區 ByteBuffer buf1 = ByteBuffer.allocate(250); ByteBuffer buf2 = ByteBuffer.allocate(500); //3. 分散讀取 ByteBuffer[] bufs = {buf1, buf2}; channel1.read(bufs); for (ByteBuffer byteBuffer : bufs) { byteBuffer.flip(); } System.out.println(new String(bufs[0].array(), 0, bufs[0].limit())); System.out.println("-----------------"); System.out.println(new String(bufs[1].array(), 0, bufs[1].limit())); //4. 聚集寫入 RandomAccessFile raf2 = new RandomAccessFile("2.txt", "rw"); FileChannel channel2 = raf2.getChannel(); channel2.write(bufs);
五、字符集 Charset:
編碼:字符串->字節數組。解碼:字節數組->字符串
1.查看Charset里都有哪些編碼:
Map<String, Charset> map = Charset.availableCharsets(); map.forEach((k,v)->{ System.out.println(k);//常見的UTF-8等等.. });
2.緩沖區編解碼:
Charset c = Charset.forName("UTF-8"); CharsetEncoder e = cs1.newEncoder(); //獲取編碼器 CharsetDecoder d = cs1.newDecoder(); //獲取解碼器 CharBuffer buf = CharBuffer.allocate(1024); buf.put("二狗子到此一游"); buf.flip(); ByteBuffer bBuf = e.encode(buf );//編碼 bBuf.flip(); //解碼 CharBuffer buf2= d.decode(bBuf); System.out.println(buf2.toString());
六、阻塞與非阻塞:
阻塞與非阻塞式的,相較於網絡通信:
1)阻塞:C端發送S端讀寫請求,S端考慮,阻塞,服務端不會做其他事情,解決方式多線程
2)非阻塞:Selector(選擇器),通道會注冊到選擇器上,Selector監控IO狀況,只有C端准備就緒