通道间的数据传输
这里主要介绍两个通道与通道之间数据传输的方式:
transferTo():把源通道的数据传输到目的通道中。
public static void main(String[] args) throws Exception { File file = new File("1.txt"); FileInputStream inputStream = new FileInputStream(file); FileChannel inputStreamChannel = inputStream.getChannel(); FileOutputStream outputStream = new FileOutputStream(new File("2.txt")); FileChannel outputStreamChannel = outputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length()); inputStreamChannel.transferTo(0, byteBuffer.limit(), outputStreamChannel); outputStream.close(); inputStream.close(); outputStreamChannel.close(); inputStreamChannel.close(); }
|
transferFrom():把来自源通道的数据传输到目的通道。
public static void main(String[] args) throws Exception { File file = new File("1.txt"); FileInputStream inputStream = new FileInputStream(file); FileChannel inputStreamChannel = inputStream.getChannel(); FileOutputStream outputStream = new FileOutputStream(new File("2.txt")); FileChannel outputStreamChannel = outputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length()); outputStreamChannel.transferFrom(inputStreamChannel,0,byteBuffer.limit()); outputStream.close(); inputStream.close(); outputStreamChannel.close(); inputStreamChannel.close(); }
|
分散读取和聚合写入
我们先看一下FileChannel的源码:
public abstract class FileChannel extends AbstractInterruptibleChannel implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel { }
|
从源码中可以看出实现了GatheringByteChannel, ScatteringByteChannel接口。也就是支持分散读取和聚合写入的操作。怎么使用呢,请看以下例子:
我们写一个main方法来实现复制1.txt文件,文件内容是:
abcdefghijklmnopqrstuvwxyz
|
代码如下:
public static void main(String[] args) throws Exception { File file = new File("1.txt"); FileInputStream inputStream = new FileInputStream(file); FileChannel inputStreamChannel = inputStream.getChannel(); FileOutputStream outputStream = new FileOutputStream(new File("2.txt")); FileChannel outputStreamChannel = outputStream.getChannel(); ByteBuffer byteBuffer1 = ByteBuffer.allocate(5); ByteBuffer byteBuffer2 = ByteBuffer.allocate(5); ByteBuffer byteBuffer3 = ByteBuffer.allocate(5); ByteBuffer[] buffers = new ByteBuffer[]{byteBuffer1, byteBuffer2, byteBuffer3}; long read; long sumLength = 0; while ((read = inputStreamChannel.read(buffers)) != -1) { sumLength += read; Arrays.stream(buffers) .map(buffer -> "posstion=" + buffer.position() + ",limit=" + buffer.limit()) .forEach(System.out::println); Arrays.stream(buffers).forEach(Buffer::flip); outputStreamChannel.write(buffers); Arrays.stream(buffers).forEach(Buffer::clear); } System.out.println("总长度:" + sumLength); outputStream.close(); inputStream.close(); outputStreamChannel.close(); inputStreamChannel.close(); }
|
打印结果:
posstion=5,limit=5 posstion=5,limit=5 posstion=5,limit=5
posstion=5,limit=5 posstion=5,limit=5 posstion=1,limit=5
总长度:26
|
可以看到循环了两次。第一次循环时,三个缓冲区都读取了5个字节,总共读取了15,也就是读满了。还剩下11个字节,于是第二次循环时,前两个缓冲区分配了5个字节,最后一个缓冲区给他分配了1个字节,刚好读完。总共就是26个字节。
这就是分散读取,聚合写入的过程。
使用场景就是可以使用一个缓冲区数组,自动地根据需要去分配缓冲区的大小。可以减少内存消耗。网络IO也可以使用,这里就不写例子演示了。
非直接/直接缓冲区
非直接缓冲区的创建方式:
static ByteBuffer allocate(int capacity)
|
直接缓冲区的创建方式:
static ByteBuffer allocateDirect(int capacity)
|
非直接/直接缓冲区的区别示意图:
从示意图中我们可以发现,最大的不同在于直接缓冲区不需要再把文件内容copy到物理内存中。这就大大地提高了性能。其实在介绍Buffer时,我们就有接触到这个概念。直接缓冲区是堆外内存,在本地文件IO效率会更高一点。
接下来我们来对比一下效率,以一个136 MB的视频文件为例:
public static void main(String[] args) throws Exception { long starTime = System.currentTimeMillis(); File file = new File("D:\\小电影.mp4"); FileInputStream inputStream = new FileInputStream(file); FileChannel inputStreamChannel = inputStream.getChannel(); FileOutputStream outputStream = new FileOutputStream(new File("D:\\test.mp4")); FileChannel outputStreamChannel = outputStream.getChannel(); ByteBuffer byteBuffer = ByteBuffer.allocateDirect(5 * 1024 * 1024); while (inputStreamChannel.read(byteBuffer) != -1) { byteBuffer.flip(); outputStreamChannel.write(byteBuffer); byteBuffer.clear(); } outputStream.close(); inputStream.close(); outputStreamChannel.close(); inputStreamChannel.close(); long endTime = System.currentTimeMillis(); System.out.println("消耗时间:" + (endTime - starTime) + "毫秒"); }
|
结果:
直接缓冲区的消耗时间:283毫秒
非直接缓冲区的消耗时间:487毫秒