關鍵點說明
-
文件打開選項:
-
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的鎖機制可以有效地管理對共享文件的并發訪問,保證數據的一致性和完整性。