java filechanel_FileChannel文件通道

FileChannel

FileChannel 可以通过 RandomAccessFile、FileInputStream、FileOutStream也可以通过FileChannel.open 获取。

其中方法write 和 read 都是通过 ByteBuffer 对象进行操作(读写)。

如果使用FileChannel.open打开文件通道时可以提供 OpenOption 来定义行为,如果需要写的话可以使用 write 和 append 模式,在不确定文件是否存在是加入 Create,这样如果不存在会自动创建。

write 和 append 有什么区别?

这两种模式声明的不是 FileChannel 的模式,而是声明那个文件的打开模式,作为 FileChannel 只顾自己position 增加,在 write 模式下文件的 postion 跟 Channel 的 position 是一致的,但是在 append 模式下文件 position 跟 Channel 的完全脱节,两者都是自顾自增加。

说到这里你可能已经想到,如果要实现每次输入内容覆盖之前的话,必须选择 Write 模式,并且每次 channel.write 前都需要将channel的 position 置为零。

文件锁 Lock

FileChannel.lock和 tryLock从文档上看一个是同步阻塞、另一个是非阻塞。

tryLock 在同一个JVM中不同线程获取时,先到先得,后到的返回null,但我在windows上测试为抛出异常:OverlappingFileLockException ,据说 Linux 上抛出【java.io.IOException:Permission denied】。

tryLock 和 lock 都提供一个API含有三个参数

lock(long position,

long size,

boolean shared)

看样子貌似可以锁住部分似的,可能跟操作系统有关,反正windows上并不行,抛出异常:java.io.IOException: 另一个程序已锁定文件的一部分,进程无法访问。

共享锁,独占锁概念上跟并发的 WriteReadLock一样可多读但只一写,写是独占的。

怎么设置?看上面API第三个参数。

lock(long position,

long size,

boolean shared) // 是否共享锁

如何判断获取到的是什么锁?

FileLock.isShared()

实现靠谱的文件锁

主要就是一个循环判断加try.catch

while (true) {

try {

lock = fc.lock();

break;

} catch (OverlappingFileLockException e) {

Thread.sleep(1 * 1000);

}

}

这可以让需要获取锁的代码块阻塞在那里,当然咯,如果想 wait 也是可以的,只是要在 release 的地方加上 notifyAll 了。

内存映射文件

这个可谓 “大杀器”,特别是大文件读写时,效率比普通IO要快N倍。据一位网友测试86M的文件进行读操作,内存映射方式只要78ms,而普通IO需要468ms,差了6倍。可见威力无穷。

为什么这么快?

普通IO是操作系统先读入到内核缓冲器,再转到用户进程私有的内存区,当然JVM进程还作了内核态和用户态的切换;而内存映射方式,是将文件直接映射到内存中的一块区域,当调用读时会抛出缺页异常,OS会读取该页数据,依次往复,因此这里少了依次内核缓冲区到私有内存之间的拷贝,所以快了不少。

内存映射模式:

read_only只读设置 ;

read_write读写都可,并且任何写操作立即反应在文件上,其他共享该文件的内存映射也能立即看到 。

private私有模式,不写时跟read_only 一样,但是写时会克隆一个独立内存区域,不会影响文件。

代码片段:

import java.io.FileInputStream;

import java.io.FileNotFoundException;

import java.io.FileOutputStream;

import java.io.IOException;

import java.nio.ByteBuffer;

import java.nio.MappedByteBuffer;

import java.nio.channels.FileChannel;

import java.nio.file.Path;

import java.nio.file.Paths;

/**

* Created by alan on 2018/12/15.

*/

public class FileChannelDemo extends OutPut {

public static void main(String[] args) {

String path = "d:/1.txt";

w(path);

try {

Path paths = Paths.get(path);

// FileChannel channel = FileChannel.open(paths);//HEX{01 02 03 04}

FileInputStream in = new FileInputStream(path);

FileChannel channel = in.getChannel();

MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());

String str = "";

for (int i = 0; i < channel.size(); i++) {

str += buffer.get() + " ";

}

out(str);

buffer.flip();

channel.close();

in.close();

} catch (IOException e) {

e.printStackTrace();

}

}

public static void w(String path) {

try {

FileOutputStream out = new FileOutputStream(path);

FileChannel channel = out.getChannel();

ByteBuffer buffer = ByteBuffer.allocate(20);

buffer.put((byte) 0x1B);

buffer.put((byte) 0x2B);

buffer.put((byte) 0x3B);

buffer.put((byte) 0x4B);

buffer.put((byte) 0x5B);

buffer.put((byte) 0xFB);

buffer.put((byte) 0xEB);

buffer.flip();

channel.write(buffer,0);

channel.close();

out.flush();

out.close();

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!如果在使用JavaFileChannel下载文件时出现内存溢出的问题,可能是因为您一次性尝试读取或写入过大的文件块导致的。以下是一些解决方法: 1. 使用ByteBuffer进行分块读取:通过创建一个较小的ByteBuffer对象,例如4KB或8KB,并使用循环读取文件的一部分,然后处理该部分数据。这样可以避免一次性将整个文件加载到内存中。 ```java FileChannel channel = new FileInputStream(sourceFile).getChannel(); ByteBuffer buffer = ByteBuffer.allocate(8 * 1024); // 8KB buffer while (channel.read(buffer) != -1) { buffer.flip(); // 处理buffer中的数据 buffer.clear(); } ``` 2. 使用内存映射文件(Memory-Mapped File):内存映射文件允许您将文件直接映射到内存中,从而可以像访问内存一样访问文件内容。这样可以避免将整个文件加载到内存中。 ```java FileChannel channel = new RandomAccessFile(sourceFile, "r").getChannel(); MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size()); // 处理buffer中的数据 ``` 3. 降低缓冲区大小:在使用FileChannel时,可以通过减小ByteBuffer的大小来降低内存消耗。较小的缓冲区大小可能会导致性能损失,但可以避免内存溢出。 ```java FileChannel channel = new FileInputStream(sourceFile).getChannel(); ByteBuffer buffer = ByteBuffer.allocate(1 * 1024); // 1KB buffer while (channel.read(buffer) != -1) { buffer.flip(); // 处理buffer中的数据 buffer.clear(); } ``` 请根据您的具体需求选择合适的解决方法。希望对您有所帮助!如果您有任何其他问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值