关键点说明
-
文件打开选项:
-
StandardOpenOption.CREATE
- 文件不存在时创建 -
StandardOpenOption.READ
/WRITE
- 读写权限 -
StandardOpenOption.APPEND
- 追加模式 -
StandardOpenOption.TRUNCATE_EXISTING
- 清空已存在文件
-
-
缓冲区操作:
-
ByteBuffer.wrap()
包装现有字节数组 -
buffer.flip()
切换读写模式 -
直接操作缓冲区提高性能
-
-
高效文件复制:
-
transferTo()
/transferFrom()
方法比传统流复制更高效
-
-
资源管理:
-
使用try-with-resources确保通道自动关闭
-
文件锁需要显式释放
-
这些示例展示了FileChannel的基本读写操作、文件锁的使用以及内存映射文件的高效操作,可以根据实际需求进行调整和扩展。
1. 基本文件读写示例
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelDemo {public static void main(String[] args) {String filePath = "test.txt";// 写入文件writeToFile(filePath, "Hello, FileChannel!");// 读取文件String content = readFromFile(filePath);System.out.println("文件内容: " + content);// 追加内容appendToFile(filePath, "\n这是追加的内容");// 再次读取System.out.println("追加后的内容: " + readFromFile(filePath));// 文件复制copyFile(filePath, "test_copy.txt");System.out.println("复制文件内容: " + readFromFile("test_copy.txt"));}// 写入文件(覆盖)public static void writeToFile(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.TRUNCATE_EXISTING)) {ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());channel.write(buffer);System.out.println("写入文件成功");} catch (IOException e) {e.printStackTrace();}}// 读取文件public static String readFromFile(String filePath) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.READ)) {ByteBuffer buffer = ByteBuffer.allocate((int) channel.size());channel.read(buffer);buffer.flip();return new String(buffer.array(), 0, buffer.limit());} catch (IOException e) {e.printStackTrace();return null;}}// 追加内容到文件public static void appendToFile(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.WRITE,StandardOpenOption.APPEND)) {ByteBuffer buffer = ByteBuffer.wrap(content.getBytes());channel.write(buffer);System.out.println("追加内容成功");} catch (IOException e) {e.printStackTrace();}}// 文件复制public static void copyFile(String sourcePath, String targetPath) {try (FileChannel source = FileChannel.open(Paths.get(sourcePath),StandardOpenOption.READ);FileChannel target = FileChannel.open(Paths.get(targetPath),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.TRUNCATE_EXISTING)) {// 使用transferTo进行高效的文件复制source.transferTo(0, source.size(), target);System.out.println("文件复制成功");} catch (IOException e) {e.printStackTrace();}}
}
2. 使用文件锁的读写示例
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class FileChannelWithLockDemo {public static void main(String[] args) {String filePath = "locked_file.txt";// 线程1 - 写入数据new Thread(() -> {writeWithLock(filePath, "线程1写入的数据");}).start();// 线程2 - 尝试写入new Thread(() -> {writeWithLock(filePath, "线程2写入的数据");}).start();}public static void writeWithLock(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.CREATE,StandardOpenOption.WRITE,StandardOpenOption.APPEND)) {// 尝试获取排他锁System.out.println(Thread.currentThread().getName() + " 尝试获取文件锁...");FileLock lock = channel.lock();try {System.out.println(Thread.currentThread().getName() + " 获取到文件锁");// 模拟耗时操作Thread.sleep(2000);// 写入数据ByteBuffer buffer = ByteBuffer.wrap((content + "\n").getBytes());channel.write(buffer);System.out.println(Thread.currentThread().getName() + " 写入完成");} finally {lock.release();System.out.println(Thread.currentThread().getName() + " 释放文件锁");}} catch (IOException | InterruptedException e) {e.printStackTrace();}}
}
3.内存映射文件示例
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;public class MemoryMappedFileDemo {public static void main(String[] args) {String filePath = "mapped_file.txt";// 写入内存映射文件writeMappedFile(filePath, "这是内存映射文件的内容");// 读取内存映射文件readMappedFile(filePath);}public static void writeMappedFile(String filePath, String content) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE)) {// 创建内存映射MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, content.getBytes().length);// 直接操作内存buffer.put(content.getBytes());System.out.println("内存映射文件写入完成");} catch (IOException e) {e.printStackTrace();}}public static void readMappedFile(String filePath) {try (FileChannel channel = FileChannel.open(Paths.get(filePath),StandardOpenOption.READ)) {MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());byte[] bytes = new byte[(int) channel.size()];buffer.get(bytes);System.out.println("读取到的内容: " + new String(bytes));} catch (IOException e) {e.printStackTrace();}}
}
FileChannel的lock详解
一、文件锁的基本概念
文件锁分为两种类型:
-
排他锁(独占锁):
-
同一时间只能有一个进程持有
-
其他进程无法获取任何类型的锁
-
-
共享锁(读锁):
-
可以被多个进程同时持有
-
但只要有进程持有共享锁,就不能获取排他锁
-
二、lock()方法的使用
1. 基本锁定方式
FileChannel channel = FileChannel.open(Paths.get("test.txt"), StandardOpenOption.READ, StandardOpenOption.WRITE);// 获取排他锁(默认)
FileLock exclusiveLock = channel.lock();// 获取共享锁(第三个参数为true)
FileLock sharedLock = channel.lock(0, Long.MAX_VALUE, true);
2. 方法参数说明
public final FileLock lock(long position, long size, boolean shared)
-
position
:锁定区域的起始位置 -
size
:锁定区域的大小(比特) -
shared
:是否为共享锁(true=共享锁,false=排他锁)
3.非阻塞尝试锁定
FileLock tryLock = channel.tryLock(); // 非阻塞版本
if (tryLock == null) {// 获取锁失败
}
tryLock.release()
四、注意事项
-
锁的有效性:
-
文件锁是建议性的(advisory),不是强制性的
-
只有遵守锁协议的进程才会受锁影响
-
操作系统可能有不同的实现方式
-
-
性能考虑:
-
频繁获取/释放锁会影响性能
-
锁定大文件区域可能降低并发性
-
-
异常处理:
-
OverlappingFileLockException
:当请求的锁区域与现有锁重叠时抛出 -
NonWritableChannelException
:尝试在只读通道上获取排他锁
-
-
平台差异:
-
Windows系统上的实现与Unix-like系统不同
-
某些网络文件系统可能不支持文件锁
-
五、适用场景
-
多进程共享文件访问控制
-
防止文件被多个写入者同时修改
-
实现简单的进程间同步机制
-
保护关键配置文件不被并发修改
正确使用FileChannel的锁机制可以有效地管理对共享文件的并发访问,保证数据的一致性和完整性。