目錄:
Java NIO 學(xué)習(xí)筆記(一)----概述,Channel/Buffer
Java NIO 學(xué)習(xí)筆記(二)----聚集和分散,通道到通道
Java NIO 學(xué)習(xí)筆記(三)----Selector
Java NIO 學(xué)習(xí)筆記(四)----文件通道和網(wǎng)絡(luò)通道
Java NIO 學(xué)習(xí)筆記(五)----路徑、文件和管道 Path/Files/Pipe
Java NIO 學(xué)習(xí)筆記(六)----異步文件通道 AsynchronousFileChannel
Java NIO 學(xué)習(xí)筆記(七)----NIO/IO 的對比和總結(jié)
Scatter / Gather 通道的聚集和分散操作
NIO 具有內(nèi)置的 scatter/gather 支持,用于描述讀取和寫入通道的操作。
- 分散(scatter)地從 Channel 中讀取是將數(shù)據(jù)讀入多個 Buffer 的操作。 因此,通道將來自通道的數(shù)據(jù)“分散”到多個緩沖區(qū)中。
- 聚集(gather)地寫入 Channel 是將來自多個緩沖區(qū)的數(shù)據(jù)寫入單個通道的操作。 因此,通道將來自多個緩沖區(qū)的數(shù)據(jù)“收集”到同一個通道中。
通道的聚集和分散操作在需要將傳輸?shù)臄?shù)據(jù)分開處理的場合非常有用,例如,如果消息由標題和正文組成,則可以將標題和正文保留在單獨的緩沖區(qū)中,這樣做可以更容易處理標題和正文。
Scattering Reads 分散讀取
是指將數(shù)據(jù)從單個通道讀入多個緩沖區(qū):

下面是一個代碼示例,演示如何執(zhí)行分散讀取:
public class ScatteringReads {
public static void main(String[] args) throws IOException {
ByteBuffer buffer1 = ByteBuffer.allocate(5); // 分配第一個緩沖區(qū),大小為 5
ByteBuffer buffer2 = ByteBuffer.allocate(128);
ByteBuffer[] buffers = {buffer1, buffer2}; // 兩個緩沖區(qū)的數(shù)組
File file = new File("D:\\test\\1.txt"); // 文件內(nèi)容是 012345678
RandomAccessFile accessFile = new RandomAccessFile(file, "rw");
FileChannel channel = accessFile.getChannel();
long data = channel.read(buffers); // 一次性把通道的數(shù)據(jù)讀入2個緩沖區(qū)
System.out.println("Read: " + data); // Read 9
System.out.println("開始讀取第一個 buffer :");
buffer1.flip(); // 將 buffer 從寫入模式切換為讀取模式
while (buffer1.hasRemaining()) {
System.out.print((char) buffer1.get()); // 每次讀取1byte,輸出 01234
}
System.out.println("\n開始讀取第二個 buffer :");
buffer2.flip();
while (buffer2.hasRemaining()) {
System.out.print((char) buffer2.get()); // 輸出 5678
}
}
}
將會輸出:
Read: 9
開始讀取第一個 buffer :
01234
開始讀取第二個 buffer :
5678
注意多個緩沖區(qū)首先插入到數(shù)組中,然后將數(shù)組作為參數(shù)傳遞給 channel.read() 方法。 然后,read()方法按照緩沖區(qū)在數(shù)組中出現(xiàn)的順序從通道寫入數(shù)據(jù)。 一旦緩沖區(qū)已滿,通道就會繼續(xù)填充下一個緩沖區(qū)。
分散讀取在移動到下一個緩沖區(qū)之前,必須先填充慢前一個緩沖區(qū),這意味著它不適合大小不固定的消息。
Gathering Writes 聚集寫入
“聚集寫入”將來自多個緩沖區(qū)的數(shù)據(jù)寫入單個通道:

一個示例:
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
//write data into buffers...
channel.write(bufferArray);
將緩沖區(qū)數(shù)組傳遞給 write() 方法,該方法按照在數(shù)組的順序?qū)懭刖彌_區(qū)的內(nèi)容到單個通道,注意僅寫入緩沖區(qū)的 position 和 limit 之間的數(shù)據(jù)。 因此,如果緩沖區(qū)的容量為 128 字節(jié),但只包含 58 字節(jié)的內(nèi)容,則只有 58 字節(jié)從該緩沖區(qū)寫入通道。 因此,與 Scattering Reads 相比,Gathering Writes 可以適應(yīng)大小不固定的數(shù)據(jù),因為它只把包含內(nèi)容部分的緩沖區(qū)寫入到通道。
Channel to Channel 通道到通道傳輸
在 NIO 中,如果其中一個通道是 FileChannel ,可以直接將數(shù)據(jù)從一個通道傳輸?shù)搅硪粋€通道。 FileChannel 類有一個 transferTo() 和 transferFrom() 方法。
transferFrom() 和 transferTo()
FileChannel 對象的 transferFrom() 方法將數(shù)據(jù)從源通道傳輸?shù)?FileChannel。 這是一個簡單的例子:
public class TransfetExample {
public static void main(String[] args) throws IOException {
RandomAccessFile fromFile = new RandomAccessFile("D:\\test\\input.txt", "rw");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("D:\\test\\receive.txt", "rw");
FileChannel toChannel = toFile.getChannel();
long position = 0;
long count = fromChannel.size();
toChannel.transferFrom(fromChannel, position, count);
}
}
參數(shù) position 和 count,告訴目標文件中開始寫入的位置以及最大傳輸?shù)淖止?jié)數(shù)(總數(shù))。 如果源通道的字節(jié)數(shù)少于 count ,則傳輸實際字節(jié)數(shù)。
此外,一些 SocketChannel 實現(xiàn)可能現(xiàn)在只傳輸 SocketChannel 在其內(nèi)部緩沖區(qū)中準備好的數(shù)據(jù) - 即使 SocketChannel 可能稍后有更多可用數(shù)據(jù)。 因此,它可能不會將請求的整個數(shù)據(jù)(count)從 SocketChannel 傳輸?shù)?FileChannel 。
transferTo() 方法的效果除了目標和參數(shù)位置不一致,其他部分同 transferFrom() 方法一樣,上面代碼如果換成執(zhí)行 fromChannel.transferTo(position, count, toChannel); input.txt 的內(nèi)容同樣會被立即復(fù)制到 receive.txt。