摘要:也支持鎖定部分內容,使用即可,其中為時,表明該鎖是一個共享鎖,可以允許多個縣城讀取文件,但阻止其他進程獲得該文件的排他鎖。當為時,表明是一個排他鎖,它將鎖住對該文件的讀寫。默認獲取的是排他鎖。
Java的NIO
BufferedReader有一個特征,就是讀取輸入流中的數據時,如果沒有讀到有效數據,程序將在此處阻塞該線程的執行(使用InputStream的read方法從流中讀取數據時,也有這樣的特性),java.io下面的輸入輸出流都是阻塞式的。不僅如此,傳統的輸入輸出流都是通過字節的移動來處理的,及時我們不直接去處理字節流,單底層的實現還是依賴于字節處理,也就是說,面向流的輸入輸出系統一次只能處理一個字節,因此面向流的輸入輸出系統通常效率都不高。
為了解決上面的問題,NIO就出現了。NIO采用內存映射文件來處理輸入輸出,NIO將文件或文件的一段區域映射到內存中,這樣就可以像訪問內存一樣來訪問文件了。(這種方式模擬了操作系統上虛擬內存的概念),通過這種方式進行輸入輸出要快得多。
Channel(通道)和Buffer(緩沖)是NIO中兩個核心對象。Channel是對傳統的輸入輸出系統的模擬,在NIO系統中所有的數據都需要通過通道傳輸,Channel與傳統的InputStream、OutputStream最大的區別就是提供了一個map()方法,通過它可以直接將“一塊數據”映射到內存中。如果說傳統IO是面向流的處理,那么NIO是面向塊的處理。
Buffer可以被理解為一個容器,它的本質是一個數組,發送到Channel中所有的對象都必須首先放到Buffer中,從而Channel中讀取的數據也必須先放到Buffer中。Buffer既可以一次次從Channel取數據,也可以使用Channel直接將文件的某塊數據映射成Buffer。
除了Channel和Buffer之外,NIO還提供了用于將Unicode字符串映射成字節序列以及逆映射操作的Charset類,也提供了用于支持非阻塞式輸入輸出的Selector類。
Buffer介紹在Buffer中有三個重要的概念:
容量(capacity):表示Buffer的大小,創建后不能呢過改變;
界限(limit):第一個不能被讀寫的緩沖區的位置,也就是后面的數據不能被讀寫;
位置(position):用于指明下一個可以被讀寫的緩沖區位置索引。當從Channel讀取數據的時候,position的值就等于讀到了多少數據。
Buffer的主要作用就是裝入數據,然后輸出數據。當裝入數據結束時,調用flip()方法,該方法將limit設為position所在位置,將position的值設為0,為輸出數據做好準備。當輸出數據結束后,調用clear()方法,將position的值設為0,limit設為capacity,為下一次的裝入數據做準備。
常用的Buffer是CharBuffer和ByteBuffer。
使用put()和get()方法進行數據的放入和讀取,分為相對和絕對兩種:
相對:從Buffer當前position位置開始讀取或者寫入數據,然后將position的值按處理元素的個數增加;
絕對:直接根據索引向Buffer中讀取和寫入數據,使用絕對方式訪問Buffer里的數據時,不會影響position的值。
代碼示例
package com.wangjun.othersOfJava; import java.nio.CharBuffer; public class NIOBufferTest { public static void main(String[] args) { //創建CharBuffer CharBuffer cb = CharBuffer.allocate(8); System.out.println("capacity:" + cb.capacity()); System.out.println("limit:" + cb.limit()); System.out.println("position:" + cb.position()); //放入元素 cb.put("a"); cb.put("b"); cb.put("c"); System.out.println("加入三個元素后,position:" + cb.position()); cb.flip(); System.out.println("執行flip()后,limit:" + cb.limit()); System.out.println("執行flip()后,position:" + cb.position()); System.out.println("取出第一個元素: " + cb.get()); System.out.println("取出第一個元素后,position:" + cb.position()); //調用clear方法 cb.clear(); System.out.println("執行clear()后,limit:" + cb.limit()); System.out.println("執行clear()后,position:" + cb.position()); System.out.println("執行clear()后,數據沒有清空,第三個值是:" + cb.get(2)); System.out.println("執行絕對讀取后,position:" + cb.position()); } }
通過allocate方法創建的是普通Buffer,還可以通過allocateDirect方法來創建直接Buffer,雖然創建成本比較高,但是讀寫快。因此適用于長期生存的Buffer,使用方法和普通Buffer類似。注意,只有ByteBuffer提供了此方法,其他類型的想用,可以將該Buffer轉成其他類型的Buffer。
Channel(通道)介紹Channel類似傳統的流對象,主要區別如下:
Channel可以直接將指定文件的部分或全部直接映射成Buffer。
程序不能直接訪問Channel中的數據,只能通過Buffer交互。
所有的Channel都不應該通過構造器來創建,而是通過傳統的InputStream、OutputStream的getChannel()方法來返回對應的Channel,不同的節點流獲取的Channel不一樣,比如FileInputStream返回的是FileChannel。
Channel常用的方法有三類:map()、read()、write()。map方法將Channel對應的部分或全部數據映射成ByteBuffer;read和write方法都有一系列的重載形式,這些方法用于從Buffer中讀取/寫入數據。
代碼示例
package com.wangjun.othersOfJava; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.nio.CharBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; /* * 將FileChannel的全部數據映射成ByteBuffer */ public class NIOChannelTest { public static void main(String[] args) throws Exception { File f = new File("NIOChannelTest.md"); //java7新特性try括號內的資源會在try語句結束后自動釋放,前提是這些可關閉的資源必須實現 java.lang.AutoCloseable 接口。 try( FileChannel inChannel = new FileInputStream(f).getChannel(); FileChannel outChannel = new FileOutputStream("a.md").getChannel(); ){ //將FileChannel的全部數據映射成ByteBuffer //map方法的三個參數:1映射模式;2,3控制將哪些數據映射成ByteBuffer MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length()); //直接將buffer的數據全部輸出,完成文件的復制: NIOChannelTest.md -> a.md outChannel.write(buffer); //以下為了輸出文件字符串內容 //使用GBK字符集來創建解碼器 Charset charset = Charset.forName("GBK"); //復原limit和position的位置 buffer.clear(); //創建解碼器 CharsetDecoder decoder = charset.newDecoder(); //使用解碼器將ByteBuffer轉成CharBuffer CharBuffer charBuffer = decoder.decode(buffer); System.out.println(charBuffer); } } }
除了上面的一次性取數據,也可以分多次取數據
//如果Channel對應的文件過大,使用map方法一次性將所有文件內容映射到內存中可能會因此性能下降 //這時候我們可以適應Channel和Buffer進行多次重復取值 public static void getDataByArray() throws Exception { try( //創建文件輸入流 FileInputStream fileInputStream = new FileInputStream("NIOChannelTest.md"); FileChannel fileChannel = fileInputStream.getChannel(); ){ //定義一個ByteBuffer對象,用于重復取水 ByteBuffer bbuff = ByteBuffer.allocate(64); while(fileChannel.read(bbuff) != -1) { bbuff.flip(); Charset charset = Charset.forName("GBK"); //創建解碼器 CharsetDecoder decoder = charset.newDecoder(); //使用解碼器將ByteBuffer轉成CharBuffer CharBuffer charBuffer = decoder.decode(bbuff); System.out.println(charBuffer); //為下一次讀取數據做準備 bbuff.clear(); } } }文件鎖
在NIO中,Java提供了文件鎖的支持,使用FileLock來支持文件鎖定功能,在FileChannel中提供lock()/tryLock()方法來獲取文件鎖FileLock對象,從而鎖定文件。lock和tryLock的區別是前者無法得到文件鎖的時候會阻塞,后者不會阻塞。也支持鎖定部分內容,使用lock(long position, long size, boolean shared)即可,其中shared為true時,表明該鎖是一個共享鎖,可以允許多個縣城讀取文件,但阻止其他進程獲得該文件的排他鎖。當shared為false時,表明是一個排他鎖,它將鎖住對該文件的讀寫。
默認獲取的是排他鎖。
代碼示例
package com.wangjun.othersOfJava; import java.io.FileOutputStream; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public class FileLockTest { public static void main(String[] args) throws Exception { try( FileOutputStream fos = new FileOutputStream("a.md"); FileChannel fc = fos.getChannel(); ){ //使用非阻塞方式對指定文件加鎖 FileLock lock = fc.tryLock(); Thread.sleep(3000); lock.release();//釋放鎖 } } }Java的NIO2
Java7對原來的NIO進行了重大改進:
提供了全面的文件IO和文件系統訪問支持;
基于異步Channel的IO。
這里先簡單介紹一下對文件系統的支持,后續繼續學習。
NIO2提供了一下接口和工具類:
Path接口:通過和Paths工具類結合使用產生Path對象,可以獲取文件根路徑、絕對路徑、路徑數量等;
Files工具類:提供了很多靜態方法,比如復制文件、一次性讀取文件所有行、判斷是否為隱藏文件、判斷文件大小、遍歷文件和子目錄、訪問文件屬性等;
FileVisitor接口:代表一個文件訪問器,Files工具類使用walkFileTree方法遍歷文件和子目錄時,都會觸發FileVisitor中相應的方法,比如訪問目錄之前、之后,訪問文件時,訪問文件失敗時;
WatchService:監控文件的變化;
NIO和IO的區別Java的NIO提供了與標準IO不同的工作方式:
Channels and Buffers(通道和緩沖區):標準的IO基于字節流和字符流進行操作的,沒有被緩存在任何地方,而NIO是基于通道(Channel)和緩沖區(Buffer)進行操作,數據總是從通道讀取到緩沖區中,或者從緩沖區寫入到通道中,因此可以前后移動流中的數據;
Asynchronous IO(異步IO):Java NIO可以讓你異步的使用IO,例如:當線程從通道讀取數據到緩沖區時,線程還是可以進行其他事情。當數據被寫入到緩沖區時,線程可以繼續處理它。從緩沖區寫入通道也類似。因為NIO將阻塞交給了后臺線程執行。而IO是阻塞的;
Selectors(選擇器):選擇器允許一個多帶帶的線程可以監聽多個數據通道((網絡連接或文件),你可以注冊多個通道使用一個選擇器,然后使用一個多帶帶的線程來“選擇”通道:這些通道里已經有可以處理的輸入,或者選擇已準備寫入的通道。這種選擇機制,使得一個多帶帶的線程很容易來管理多個通道。但付出的代價是解析數據可能會比從一個阻塞流中讀取數據更復雜。?
使用場景NIO
優勢在于一個線程管理多個通道;但是數據的處理將會變得復雜;
如果需要管理同時打開的成千上萬個連接,這些連接每次只是發送少量的數據,采用這種;
傳統的IO
適用于一個線程管理一個通道的情況;因為其中的流數據的讀取是阻塞的;
如果需要管理同時打開不太多的連接,這些連接會發送大量的數據;
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/71093.html
摘要:異步可以讓你異步的使用,例如當線程從通道讀取數據到緩沖區時,線程還是可以進行其他事情。當數據被寫入到緩沖區時,線程可以繼續處理它。因此,單個的線程可以監聽多個數據通道。下面是系列文章的目錄概述通道之間的數據傳輸與原文譯者郭蕾校對方騰飛 Java NIO(New IO)是一個可以替代標準Java IO API的IO API(從Java 1.4開始),Java NIO提供了與標準IO不同的...
摘要:線程之間的切換對于操作系統來說是昂貴的。因此,單線程可以監視多個通道中的數據。當方法返回后,線程可以處理這些事件。 一 NIO簡介 Java NIO 是 java 1.4 之后新出的一套IO接口,這里的的新是相對于原有標準的Java IO和Java Networking接口。NIO提供了一種完全不同的操作方式。 NIO中的N可以理解為Non-blocking,不單純是New。 它支持面...
摘要:從通道進行數據寫入創建一個緩沖區,填充數據,并要求通道寫入數據。三之通道主要內容通道介紹通常來說中的所有都是從通道開始的。從中選擇選擇器維護注冊過的通道的集合,并且這種注冊關系都被封裝在當中停止選擇的方法方法和方法。 由于內容比較多,我下面放的一部分是我更新在我的微信公眾號上的鏈接,微信排版比較好看,更加利于閱讀。每一篇文章下面我都把文章的主要內容給列出來了,便于大家學習與回顧。 Ja...
摘要:簡介是由引進的異步由以下幾個核心部分組成和的對比和的區別主要體現在三個方面基于流而基于操作是阻塞的而操作是非阻塞的沒有概念而有概念基于與基于傳統的是面向字節流或字符流的而在中我們拋棄了傳統的流而是引入了和的概念在中我只能從中讀取數據到中或將 簡介 Java NIO 是由 Java 1.4 引進的異步 IO.Java NIO 由以下幾個核心部分組成: Channel Buffer Se...
摘要:學習和掌握技術已經不是一個攻城獅的加分技能,而是一個必備技能。是雙向的,不僅可以讀取數據還能保存數據,程序不能直接讀寫通道,只與緩沖區交互為了讓大家不被高并發與大量連接處理問題所困擾,動力節點推出了高效處理模型應用教程。 大家肯定了解Java IO, 但是對于NIO一般是陌生的,而現在使用到NIO的場景越來越多,很多技術框...
摘要:后改良為用線程池的方式代替新增線程,被稱為偽異步。最大的問題是阻塞,同步。每次請求都由程序執行并返回,這是同步的缺陷。這些都會被注冊在多路復用器上。多路復用器提供選擇已經就緒狀態任務的能力。并沒有采用的多路復用器,而是使用異步通道的概念。 Netty是一個提供異步事件驅動的網絡應用框架,用以快速開發高性能、高可靠的網絡服務器和客戶端程序。Netty簡化了網絡程序的開發,是很多框架和公司...
閱讀 875·2021-11-22 09:34
閱讀 1011·2021-10-08 10:16
閱讀 1821·2021-07-25 21:42
閱讀 1793·2019-08-30 15:53
閱讀 3524·2019-08-30 13:08
閱讀 2183·2019-08-29 17:30
閱讀 3346·2019-08-29 17:22
閱讀 2180·2019-08-29 15:35