您的位置 首页 java

JAVA多线程并发写FileChannel是线程安全的吗?如何实现零拷贝?

1、多线程并发写FileChannel是线程安全的?

多线程同步并发安全的,看源码,需要了解一点,就是说,他是基于synchronized做线程同步的,他就可以保证多线程调用同一个流的getChannel()方法的时候,他是可以保证多线程并发安全的。读写的时候也是加了synchronized做线程同步的。

FileLock lock = channel.lock(0, Integer.MAX_VALUE, false);

FileChannel给我们提供了一个功能,就是所谓的文件锁,你可以对文件上锁,共享锁,独占锁,如果对文件是上共享锁的话,此时你可以读文件,别人也可以读文件,别人也可以上共享锁。

如果有人上了独占锁,别人就不可以上独占锁了,也不能上共享锁,这个时候只能是上独占锁的人自己可以读和写,其他人应该就不可以读和写了。

他加锁是可以支持针对文件的position开始的size字节数,分段加锁,就对文件的某个段(region)进行加锁。这个东西是跟底层的操作系统是有关系的,如果操作系统不支持共享锁,就会自动转换为独占锁。

2、零拷贝提供了两种方式分别是

mmap+write方式

使用mmap+write方式代替原来的read+write方式,mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系这样就可以省掉原来内核read缓冲区copy数据到用户缓冲区,但是还是需要内核read缓冲区将数据copy到内核socket缓冲区。

java nio提供的FileChannel提供了map()方法,该方法可以在一个打开的文件和MappedByteBuffer之间建立一个虚拟内存映射,MappedByteBuffer继承于ByteBuffer,类似于一个基于内存的缓冲区,只不过该对象的数据元素存储在磁盘的一个文件中,调用get()方法会从磁盘中获取数据,此数据反映该文件当前的内容,调用put()方法会更新磁盘上的文件,并且对文件做的修改对其他阅读者也是可见的。

map()方法的源码大致意思就是通过native方法获取内存映射的地址,如果失败,手动gc再次映射,最后通过内存映射的地址实例化出MappedByteBuffer,MappedByteBuffer本身是一个抽象类,其实这里真正实例化出来的是DirectByteBuffer。

DirectByteBuffer继承于MappedByteBuffer,从名字就可以猜测出开辟了一段直接的内存,并不会占用jvm的内存空间;上一节中通过Filechannel映射出的MappedByteBuffer其实际也是DirectByteBuffer,当然除了这种方式,也可以手动开辟一段空间:

ByteBuffer directByteBuffer = ByteBuffer.allocateDirect(100);

如上开辟了100字节的直接内存空间,经常需要从一个位置将文件传输到另外一个位置,FileChannel提供了transferTo()方法用来提高传输的效率。

sendfile方式(linux)

sendfile系统调用在内核版本2.1中被引入,目的是简化通过网络在两个通道之间进行的数据传输过程。sendfile系统调用的引入,不仅减少了数据复制,还减少了上下文切换的次数。

数据传送只发生在内核空间,所以减少了一次上下文切换;但是还是存在一次copy,能不能把这一次copy也省略掉,Linux2.4内核中做了改进,将Kernel buffer中对应的数据描述信息(内存地址,偏移量)记录到相应的socket缓冲区当中,这样连内核空间中的一次cpu copy也省掉了。

3、用FileChannel真可以立即写入数据到磁盘吗?

不是的,刚写完以后,数据其实是停留在os cache上的,操作系统自己管理的一个内存区域,是一个读写的内存缓冲区。

kafka、elasticsearch写数据到磁盘的时候,都是先写入os cache的,后面的操作系统自己不定时的会决定把os cache里的数据写入到磁盘上去。

channel.force(true);//把文件相关的数据强制刷入磁盘上去,避免宕机数据丢失。如果文件不在本地设备上,则不提供此类保证。

文章来源:智云一二三科技

文章标题:JAVA多线程并发写FileChannel是线程安全的吗?如何实现零拷贝?

文章地址:https://www.zhihuclub.com/174439.shtml

关于作者: 智云科技

热门文章

网站地图