Java的文件拷贝方式&Buffer

1. 利用 java.io 类库,直接为源文件构建一个 FileInputStream 读取,然后再为目标文件构建一个 FileOutputStream,完成写入工作。

2. 利用 java.nio 类库提供的 transferTo 或 transferFrom 方法实现。

3. Java标准类java.nio.file.Files.copy提供了几种Files.copy的实现。

        对于 Copy 的效率,这个其实与操作系统和配置等情况相关,总体上来说,NIO的transferTo/From 的方式可能更快,因为它更能利用现代操作系统底层机制,避免不必要拷贝和上下文切换

拷贝实现机制:

        用户态空间(User Space)和内核态空间(Kernel Space) ——上一篇文章有涉及

        当我们使用输入输出流进行读写时,实际上是进行了多次上下文切换,比如应用读取数据时,先在内核态将数据从磁盘读到内核缓存,再切换到用户态将数据从内核缓存读取到用户缓存。写入操作也是类似,仅仅是步骤相反。 这种方式会带来一定的额外开销,可能会降低IO效率                   ——可参考上一篇文章的IO流程进行理解

        而基于NIO的 transferTo的实现方式,在Linux和Unix上,则会使用到零拷贝技术,数据传输并不需要用户态参与,省去了上下文切换的开销和不必要的内存拷贝,进而可能提高应用拷贝性能。transferTo不仅仅可以用在文件拷贝中,与其类似,读取磁盘文件,然后进行Socket发送,同样可以享受到性能和扩展性的提高

 注意:Java标准类库提供的copy方法 不仅仅是支持文件之间操作,没有人限定输入输出流一定是针对文件的,是很实用的工具方法。最常见的 copy 方法其实不是利用 transferTo,而是本地技术实现的用户态拷贝。

提高类似拷贝等IO操作的性能的原则:

        1. 在程序中,使用缓存等机制,合理减少IO次数(在网络通信中,如TCP传输,window大小也可以看做是类似思路)   ——所以redis、memorycache等技术的由来

        2. 使用transferTo 等机制,减少上下文切换和额外IO操作。尽量减少不必要的转换过程,比如编解码;对象序列化和反序列化,比如操作文本文件或者网络通信。如果不是过程中需要使用文本信息,可以考虑不要将二进制信息转换为字符串,直接传输二进制信息。

NIO Buffer :

        Buffer(块)是NIO操作数据的基本工具,Java为每种原始数据类型都提供了相应的Buffer实现(布尔除外)。一个 Buffer 对象是固定数量的数据的容器,其作用是一个存储器,或者分段运输区,在这里,数据可被存储并在之后用于检索。缓冲区可以被写满或释放。对于每个非布尔原始数据类型都有一个缓冲区类,即 Buffer 的子类有:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer 和 ShortBuffer

Buffer基本属性:

 

 Capacity(容量):

        它反映这个 Buffer 到底有多大,也就是数组的长度。缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。比如:

 

 可直接执行此静态方法 ByteBuffer byteBuffer = ByteBuffer.allocate(1024)  设置ByteBuffer长度为1024,参数capacity为int类型。

limit(上界):

        操作的限额(int类型),缓冲区的第一个不能被读或写的元素。缓冲创建时,limit 的值等于 capacity 的值。此时 limit=capacity = 1024,如果我们在程序中设置了 limit = 512,Buffer 的容量为 1024,但是从 512 之后既不能读也不能写,因此可以理解成,Buffer 的实际可用大小为 512;在读取或者写入时,limit的意义也可不一样,比如:读取操作时,可以将limit设置到所容纳数据的上限;写入时,可设置容量或容量以下的可写限度。

 position(位置):

        要操作数据的起始位置,也可以理解为下一个要被读或写的元素的索引。位置会自动由相应的 get() 和 put() 函数更新。如果我们往缓存中放入"hello",此时position的位置就是5,但是要读出hello,就要把positon置为0,并且limit的值为5,api中flip()函数当我们从写模式切换到读模式的时候使用。

 Mark(标记):

        记录上一次position的位置(备忘录),默认是0,往往不是必须的。假设缓冲区中有 10 个元素,position 目前的位置为 2(也就是如果get的话是第三个元素),现在只想发送 6 - 10 之间的缓冲数据,此时我们可以 buffer.mark(buffer.position()),即把当前的 position 记入 mark 中,然后 buffer.postion(6),此时发送给 channel 的数据就是 6 - 10 的数据。发送完后,我们可以调用 buffer.reset() 使得 position = mark,因此这里的 mark 只是用于临时记录一下位置用的。

Buffer基本操作:

        创建一个ByteBuffer,放入数据,capacity是缓冲区大小,position是0,limit默认就是capacity的大小,当写入字节数据时,position值跟着上涨,但是它不可能超过limit的大小。如果想把之前写入的数据读取出来,需要调用flip()方法,将position设置为0,limit设置为以前的position那里。如果还想从头读一遍,可以调用rewind,让limit不变,position再次设置为0。

 【  Channel :   既可以从同道中人读取数据,也可以写数据到通道。流的读写通常是单向的,通道可以异步的读写。通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。FileChannel:从文件中读写数据 、 DatagramChannel:通过UDP读写网络中的数据、SocketChannel:通过TCP读写网络中的数据 、ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel   】


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Laughing_Xie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值