java nio教程_Java NIO教程
java nio教程
1.簡(jiǎn)介
Java NIO是Java 1.4引入的一個(gè)庫。 自從Java NIO推出以來,它提供了另一種方法來處理I / O和網(wǎng)絡(luò)事務(wù)。 它被認(rèn)為是Java網(wǎng)絡(luò)和Java IO庫的替代方法。 開發(fā)Java NIO的目的是使輸入和輸出的事務(wù)異步和非阻塞。 阻塞和非阻塞IO的概念將在后面的部分中介紹。
目錄
1.簡(jiǎn)介 2. IO中的術(shù)語2. Java NIO中的術(shù)語
Java NIO在使用Java的I / O處理中引入了許多新術(shù)語。 在較早的場(chǎng)景中,Java I / O基于字符流和字節(jié)流。 但是,使用Java NIO,現(xiàn)在可以通過通道讀取和處理數(shù)據(jù)-通過Channel進(jìn)入緩沖區(qū)或通過緩沖區(qū)進(jìn)入通道。 在本節(jié)中,我們將討論與Java NIO相關(guān)的各種術(shù)語,以幫助我們更好地理解進(jìn)一步的教程。
2.1阻止輸入和輸出
使用字符流和字節(jié)流,文件被加載到JVM內(nèi)存中并被鎖定以進(jìn)行讀取或?qū)懭搿?這導(dǎo)致其他試圖讀取同一文件的程序處于阻塞狀態(tài)。 盡管有可用的處理能力,但由于程序被迫等待,因此這種情況浪費(fèi)了CPU的能力。 讀取或?qū)懭霐?shù)據(jù)的這種安排稱為阻塞輸入和輸出。
2.2無阻塞輸入和輸出
隨著無阻塞輸入和輸出進(jìn)入畫面,數(shù)據(jù)開始被讀取到通道中。 在無阻塞的輸入和輸出布置中,JVM使用通道來緩沖數(shù)據(jù)。 這些通道允許動(dòng)態(tài)讀取數(shù)據(jù),而不會(huì)阻止文件供外部使用。 通道是一塊緩沖的內(nèi)存,一旦讀取了先前的緩沖數(shù)據(jù),就將其填充。 這樣可以確保在完整的讀取周期中不會(huì)阻止該文件,并且其他程序也可以對(duì)該文件進(jìn)行必要的訪問。
Java NIO主要涉及三個(gè)術(shù)語:
- 頻道
- 選擇器
- 緩沖液
這些術(shù)語將在本文中進(jìn)一步聲明。
3. Java NIO –術(shù)語
如上所述,Java非阻塞IO在通道和緩沖區(qū)上工作。 在本節(jié)中,我們將嘗試?yán)斫膺@些術(shù)語以及其他術(shù)語選擇器。 這些術(shù)語對(duì)于遵循后續(xù)教程很重要。
3.1緩沖區(qū)
緩沖區(qū)是固定的一塊內(nèi)存,用于在將數(shù)據(jù)讀入通道之前存儲(chǔ)該數(shù)據(jù)。 緩沖區(qū)可確保預(yù)定義大小的數(shù)據(jù),以加快文件,輸入和數(shù)據(jù)流的讀取速度。 緩沖區(qū)的大小可配置為2到冪n的塊。
根據(jù)輸入類型,可以使用各種類型的緩沖區(qū):
- ByteBuffer:用于按字節(jié)讀取字符流或文件
- CharBuffer:用于讀取完整ASCII集內(nèi)的字符
- DoubleBuffer:專門用于雙重?cái)?shù)據(jù)值,例如來自傳感器的讀數(shù)
- FloatBuffer:用于讀取恒定數(shù)據(jù)流,用于分析之類的目的
- LongBuffer:用于讀取長(zhǎng)數(shù)據(jù)類型的值
- IntBuffer:用于讀取分?jǐn)?shù)或結(jié)果的整數(shù)值
- ShortBuffer:用于讀取短整數(shù)值
每個(gè)緩沖區(qū)都有其特定用途。 通常用于文件的緩沖區(qū)是ByteBuffer和CharBuffer。 創(chuàng)建字節(jié)緩沖區(qū)的簡(jiǎn)短示例如下所示。
RandomAccessFile aFile = new RandomAccessFile("src/data.txt", "rw"); FileChannel inChannel = aFile.getChannel(); ByteBuffer buf = ByteBuffer.allocate(48);在上面的代碼中,正在創(chuàng)建48個(gè)字節(jié)的緩沖區(qū)。 必須指定緩沖區(qū)大小。 第三行將48字節(jié)的存儲(chǔ)空間分配給緩沖區(qū)buf。 這樣可以確保將必要的內(nèi)存預(yù)先分配給緩沖區(qū)。 在繼續(xù)使用它們進(jìn)行通道讀寫之前,有必要了解緩沖區(qū)的讀寫過程。 下圖顯示了從文件讀取字節(jié)的典型緩沖區(qū)。
緩沖區(qū)讀取
可以看出,緩沖區(qū)讀取并向左推送第一個(gè)字節(jié)。 緩沖區(qū)是后進(jìn)先出類型的內(nèi)存分配。 因此,當(dāng)您希望使用緩沖區(qū)讀取文件時(shí),必須先翻轉(zhuǎn)它才能讀取文件。 沒有翻轉(zhuǎn),數(shù)據(jù)將以相反的順序輸出。 為了翻轉(zhuǎn)緩沖區(qū),需要執(zhí)行以下簡(jiǎn)單代碼行:
buf.flip();buffer.flip()
一旦將數(shù)據(jù)讀入緩沖區(qū),就該實(shí)際獲取已讀取的數(shù)據(jù)了。 為了讀取數(shù)據(jù),使用了buf.get()函數(shù)。 此函數(shù)調(diào)用在每次調(diào)用時(shí)都讀取一個(gè)字節(jié)/字符/數(shù)據(jù)包,具體取決于緩沖區(qū)的類型。 讀取可用數(shù)據(jù)后,還需要在下一次讀取之前清理緩沖區(qū)。 必須進(jìn)行清理以確保釋放空間以讀取更多數(shù)據(jù)。 為了清洗緩沖區(qū),有兩種可行的方法-清除緩沖區(qū)或壓縮緩沖區(qū)。
要清除緩沖區(qū),請(qǐng)執(zhí)行命令buf.clear() 。 要壓縮緩沖區(qū),請(qǐng)使用命令buf.compact() 。 這兩個(gè)命令最終都會(huì)做同樣的事情。 但是, compact()僅清除使用函數(shù)buf.get()讀取的數(shù)據(jù)。 因此,當(dāng)我們需要繼續(xù)優(yōu)化緩沖區(qū)使用的內(nèi)存量時(shí)使用它。
3.1.1緩沖區(qū)屬性
緩沖區(qū)本質(zhì)上具有3個(gè)屬性:
在緩沖區(qū)寫入期間,緩沖區(qū)位置是當(dāng)前字節(jié)正在寫入的位置。 在緩沖區(qū)讀取過程中,緩沖區(qū)位置是從中讀取字節(jié)的位置。 隨著我們進(jìn)行讀取或?qū)懭氩僮?#xff0c;緩沖區(qū)位置不斷動(dòng)態(tài)變化。
在緩沖區(qū)寫入過程中,緩沖區(qū)限制是可以寫入緩沖區(qū)的最大數(shù)據(jù)大小。 本質(zhì)上,緩沖區(qū)限制和緩沖區(qū)容量在緩沖區(qū)寫入期間是同義詞。 但是,在緩沖區(qū)讀取期間,緩沖區(qū)限制是可從緩沖區(qū)讀取的可用字節(jié)數(shù)。 因此,當(dāng)彈出字節(jié)時(shí),緩沖區(qū)限制將繼續(xù)減小。
緩沖區(qū)容量是可以在任何時(shí)間點(diǎn)寫入緩沖區(qū)或從緩沖區(qū)讀取的最大數(shù)據(jù)。 因此,上面分配的大小48也稱為緩沖區(qū)容量。
3.1.2讀取和寫入緩沖區(qū)
緩沖區(qū)本質(zhì)上是一種存儲(chǔ)數(shù)據(jù)的介質(zhì),直到線程從緩沖區(qū)中讀取數(shù)據(jù)并請(qǐng)求新數(shù)據(jù)為止。 從輸入源讀取數(shù)據(jù)的主要步驟是將數(shù)據(jù)讀取到緩沖區(qū)中。 為了將數(shù)據(jù)讀入緩沖區(qū),使用了如下所示的代碼片段。
FileChannel inChannel = aFile.getChannel(); System.out.println("Created file Channel.."); ByteBuffer buf = ByteBuffer.allocate(48); int bytesRead = inChannel.read(buf); 在上面的代碼中,我們創(chuàng)建一個(gè)FileChannel來從文件中讀取數(shù)據(jù),并創(chuàng)建一個(gè)緩沖區(qū)buf來保存數(shù)據(jù)。 然后使用語句inChannel.read(buf)將緩沖區(qū)用于讀取通道的數(shù)據(jù)。 執(zhí)行此語句后,緩沖區(qū)現(xiàn)在將保留多達(dá)48個(gè)字節(jié)的可用數(shù)據(jù)。
為了開始讀取數(shù)據(jù),您使用了一個(gè)簡(jiǎn)單的語句buf.get() 。
3.1.3標(biāo)記并重置緩沖區(qū)
在讀取過程中,通常需要重復(fù)從特定位置讀取數(shù)據(jù)。 在正常情況下,一旦您從緩沖區(qū)中獲取數(shù)據(jù),就認(rèn)為該數(shù)據(jù)已消失。 但是,可以在特定的索引處標(biāo)記緩沖區(qū),從而可以再次從特定位置讀取緩沖區(qū)。 下面的代碼演示了如何完成此操作。
buffer.mark();char x = buffer.get(); char y = buffer.get(); char z = buffer.get(); char a = buffer.get(); //Do something with above databuffer.reset(); //set position back to mark.標(biāo)記和重置的主要應(yīng)用是重復(fù)分析數(shù)據(jù),重復(fù)重復(fù)信息,發(fā)送命令特定次數(shù)甚至更多。
3.2頻道
通道是非阻塞IO的主要介質(zhì)。 通道類似于可用于阻止IO的流。 這些通道支持網(wǎng)絡(luò)數(shù)據(jù)以及文件數(shù)據(jù)IO。 當(dāng)需要時(shí),通道從緩沖區(qū)讀取數(shù)據(jù)。 緩沖區(qū)將保存數(shù)據(jù),直到從緩沖區(qū)讀取數(shù)據(jù)為止。
通道具有多種實(shí)現(xiàn)方式,具體取決于要讀取的數(shù)據(jù)。 以下列出了可用于渠道的標(biāo)準(zhǔn)實(shí)現(xiàn):
- FileChannel:用于從文件讀取數(shù)據(jù)和向文件寫入數(shù)據(jù)
- DatagramChannel:用于使用UDP數(shù)據(jù)包通過網(wǎng)絡(luò)進(jìn)行數(shù)據(jù)交換
- SocketChannel:用于通過TCP套接字交換數(shù)據(jù)的TCP通道
- ServerSocketChannel:類似于Web服務(wù)器的實(shí)現(xiàn),它通過特定的TCP端口偵聽請(qǐng)求。 它為每個(gè)新連接創(chuàng)建一個(gè)新的SocketChannel實(shí)例
從通道名稱可以理解,除了文件IO之外,它們還涵蓋UDP + TCP網(wǎng)絡(luò)IO流量。 與可以在特定時(shí)刻讀取或?qū)懭氲腟treams不同,同一Channel可以無縫讀取和寫入資源。 通道支持異步讀寫,這確保了在不妨礙代碼執(zhí)行的情況下讀取數(shù)據(jù)。 上面討論的緩沖區(qū)支持通道的這種異步操作。
3.2.1通道分散和聚集
Java NIO固有地支持?jǐn)?shù)據(jù)分散和收集,以便從多個(gè)緩沖區(qū)讀取數(shù)據(jù)或?qū)?shù)據(jù)寫入多個(gè)緩沖區(qū)。 Java NIO足夠智能,可以管理多個(gè)緩沖區(qū)中的讀取和寫入。
Java NIO分散用于將通道中的讀取分散到多個(gè)緩沖區(qū)中。 它的代碼實(shí)現(xiàn)非常簡(jiǎn)單。 您需要做的就是添加一個(gè)緩沖區(qū)數(shù)組作為讀取的參數(shù)。 下面顯示了相同的代碼段。
在上面的代碼中,我們創(chuàng)建了兩個(gè)每個(gè)128字節(jié)的緩沖區(qū)。 注意這里我們創(chuàng)建了一個(gè)包含兩個(gè)緩沖區(qū)的數(shù)組。 該數(shù)組作為參數(shù)進(jìn)一步傳遞給讀取的通道。 通道將數(shù)據(jù)讀入第一個(gè)緩沖區(qū),直到達(dá)到緩沖區(qū)容量。 達(dá)到緩沖區(qū)容量后,通道將自動(dòng)切換到下一個(gè)緩沖區(qū)。 因此,讀取的通道被分散而對(duì)線程沒有任何影響。
Java NIO收集也以類似的方式工作。 讀取到多個(gè)緩沖區(qū)的數(shù)據(jù)也可以收集并寫入單個(gè)通道。 下面的代碼片段做了類似的事情。
該代碼類似于分散讀取。 需要理解的是信息的寫入順序。 信息從第一個(gè)緩沖區(qū)開始寫入通道。 達(dá)到第一個(gè)緩沖區(qū)的限制后,通道會(huì)自動(dòng)切換到下一個(gè)緩沖區(qū)。 重要的是要注意在此寫入過程中不會(huì)發(fā)生翻轉(zhuǎn)。 如果需要在寫入之前翻轉(zhuǎn)緩沖區(qū),則需要在將緩沖區(qū)分配給數(shù)組之前完成。
3.2.2頻道轉(zhuǎn)移
顧名思義,通道傳輸是將數(shù)據(jù)從一個(gè)通道傳輸?shù)搅硪粋€(gè)通道的過程。 可以從通道緩沖區(qū)的特定位置進(jìn)行通道傳輸。 但是,在位置值設(shè)置為零的情況下,可以將完整的輸入源復(fù)制或復(fù)制到指定的輸出目標(biāo)。 例如,在關(guān)鍵字和文本編輯器之間建立通道,將使您能夠?qū)⑤斎霃逆I盤連續(xù)傳輸?shù)轿谋揪庉嬈鳌?
為了促進(jìn)通道傳輸,Java NIO配備了兩個(gè)函數(shù),即transferFrom()和transferTo() 。 這些功能的用途從它們的標(biāo)識(shí)符中很清楚。 讓我們以一個(gè)例子來更好地理解這些功能。
我們將使用上面創(chuàng)建的data.txt文件作為輸入源。 我們會(huì)將數(shù)據(jù)從該文件傳輸?shù)叫挛募?strong>output.txt 。 下面的代碼使用transferFrom()方法調(diào)用執(zhí)行相同的操作。
ChannelTransfer.java
import java.io.RandomAccessFile; import java.nio.channels.FileChannel;public class ChannelTransfer {public static void main(String[] args) {try {RandomAccessFile copyFrom = new RandomAccessFile("src/data.txt", "rw");FileChannel fromChannel = copyFrom.getChannel();RandomAccessFile copyTo = new RandomAccessFile("src/output.txt", "rw");FileChannel toChannel = copyTo.getChannel();long count = fromChannel.size();toChannel.transferFrom(fromChannel, 0, count);} catch (Exception e) {System.out.println("Error: " + e);}} }從上面的代碼中可以看出,通道fromChannel用于從data.txt中讀取數(shù)據(jù)。 toChannel用于從位置0開始的fromChannel獲取數(shù)據(jù)。重要的是要注意,整個(gè)文件都是使用FileChannel復(fù)制的。 但是,在SocketChannel某些實(shí)現(xiàn)中,情況可能并非如此。 在這種情況下,僅復(fù)制在傳輸時(shí)可在緩沖區(qū)中讀取的數(shù)據(jù)。 此行為是由于SocketChannel實(shí)現(xiàn)的動(dòng)態(tài)性質(zhì)引起的。
transferTo的實(shí)現(xiàn)非常相似。 唯一需要做的更改是方法調(diào)用將使用源對(duì)象完成,而目標(biāo)通道對(duì)象將是方法調(diào)用中的一個(gè)參數(shù)。
3.3選擇器
顧名思義,選擇器用于從多個(gè)通道中選擇一個(gè)通道。 當(dāng)您計(jì)劃使用單個(gè)線程并行管理多個(gè)資源時(shí),Java NIO中的選擇器特別有用。 選擇器充當(dāng)線程和開放通道之間的橋梁。 選擇器通常在預(yù)期線程流量較低但需要使用多個(gè)資源的情況下使用。 選擇器可能扮演的角色的示意圖設(shè)計(jì)如下所示。
NIO選擇器
選擇器的創(chuàng)建非常簡(jiǎn)單。 下面的代碼段說明了如何創(chuàng)建選擇器以及如何在選擇器中注冊(cè)頻道。
Selector myFirstSelector = Selector.open(); //opens the selector SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); SelectionKey selectorKey = channel.register(myFirstSelector, SelectionKey.OP_READ); 上面的代碼創(chuàng)建一個(gè)SocketChannel ,將其配置為非阻塞并將其注冊(cè)到選擇器。 請(qǐng)注意,我們使用了SocketChannel 。 選擇器需要可配置為非阻塞的通道。 因此,選擇器不能與FileChannel一起使用。
需要注意的另一點(diǎn)是注冊(cè)SocketChannel的第二個(gè)參數(shù)。 該參數(shù)指定我們要監(jiān)視的通道事件。 選擇器等待事件,并在事件觸發(fā)時(shí)更改其狀態(tài)。 這些事件在下面列出:
可以使用如下所示的靜態(tài)常量來配置這些事件:
選擇器為我們提供了預(yù)定義的功能,以檢查這些事件的發(fā)生。 以下方法調(diào)用是不言自明的,可用于監(jiān)視這些事件的發(fā)生。
key.isAcceptable(); key.isConnectable(); key.isReadable(); key.isWritable();因此,當(dāng)我們計(jì)劃用較少的線程管理多個(gè)資源時(shí),選擇器非常有用。
4. NIO頻道
4.1文件通道
這種類型的通道通常用于讀取和寫入文件。 下面顯示了用于創(chuàng)建通道并將其用于讀寫的示例代碼
ChannelRW.java
import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel;public class ChannelRW {public static void main(String[] args) {System.out.println("Starting the file read..");try {RandomAccessFile aFile = new RandomAccessFile("src/data.txt", "rw");FileChannel inChannel = aFile.getChannel();System.out.println("Created file Channel..");ByteBuffer buf = ByteBuffer.allocate(48);int bytesRead = inChannel.read(buf);while (bytesRead != -1) {System.out.println("\nRead " + bytesRead);buf.flip();while(buf.hasRemaining()){System.out.print((char) buf.get());}buf.clear();bytesRead = inChannel.read(buf);}aFile.close();}catch(Exception e) {System.out.println("Error:"+e);}} }data.txt
Hello, This is my first NIO read. I am reading multiple lines. The code is implemented for Javacodegeeks by Abhishek Kothari在上面的代碼中,我們?cè)噲D讀取如上所述創(chuàng)建的data.txt文件。 正在使用Java NIO庫讀取該文件。 文件讀取過程涉及多個(gè)步驟。 下面顯示了針對(duì)它的逐步代碼說明。
上面代碼的輸出如下所示:
Starting the file read.. Created file Channel..Read 48 Hello, This is my first NIO read. I am reading m Read 48 ultiple lines. The code is implemented for Javac Read 28 odegeeks by Abhishek Kothari可見,數(shù)據(jù)以48個(gè)字節(jié)的塊讀取,并且每次出現(xiàn)“ Read 48 ”語句以顯示已讀取的字節(jié)數(shù)。 可以看出,當(dāng)?shù)竭_(dá)文件末尾時(shí),只能讀取28個(gè)字節(jié),因此返回計(jì)數(shù)28。
4.2 SocketChannel
這種類型的通道用于連接到http套接字。 連接到套接字的簡(jiǎn)單代碼如下所示:
SocketChannel sc = SocketChannel.open(); sc.connect(new InetSocketAddress("https://javacodegeeks.com", 8080));套接字通道在執(zhí)行方法調(diào)用時(shí)連接到指定的URL。 為了關(guān)閉開放通道,只需執(zhí)行如下所示的相應(yīng)方法調(diào)用即可。
sc.close();從SocketChannel讀取數(shù)據(jù)的過程類似于FileChannel 。
4.3 ServerSocketChannel
服務(wù)器套接字通道用于讀取來自套接字客戶端的套接字?jǐn)?shù)據(jù)。 可以使用以下代碼段將該通道配置為讀取。
ServerSocketChannel ssc = ServerSocketChannel.open();ssc.socket().bind(new InetSocketAddress(8080));while(true){SocketChannel sc =ssc.accept();//do something with sc... }上面的代碼打開服務(wù)器端套接字,并允許來自外部SocketChannel客戶端的套接字連接。 從上面的代碼可以理解, ServerSocketChannel僅需要端口號(hào)即可啟動(dòng)套接字服務(wù)器。 一旦啟動(dòng),它可以創(chuàng)建自己的SocketChannel來接受來自其他套接字的數(shù)據(jù)。 這就是使用Java NIO進(jìn)行無阻塞Socket IO連接的方式。
5.結(jié)論
本文詳細(xì)討論了Java NIO的各種術(shù)語。 它說明了使用各種NIO通道以非阻塞方式讀取和寫入數(shù)據(jù)的過程。 關(guān)于Java NIO庫,例如DatagramChannel,Pipes,Async通道等,還有更多的要探索。
翻譯自: https://www.javacodegeeks.com/2018/07/java-nio-tutorial.html
java nio教程
總結(jié)
以上是生活随笔為你收集整理的java nio教程_Java NIO教程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redmi K70系列细节曝光:最早11
- 下一篇: java.awt.api_Java SE