大檔案複製若能利用多核 CPU 並行讀寫,效能提升顯著。RandomAccessFile 支援隨機定位讀寫,是實現多執行緒分段複製的核心工具。本文展示兩種分段策略的完整實現代碼。
[start, end)seek(start) 定位讀寫指標end 結束package com.dragon.java.multithreadcopy;
import java.io.File;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.print("請輸入檔案路徑:");
File srcFile = new File(scanner.next());
System.out.print("請輸入執行緒數:");
int n = scanner.nextInt();
if (!srcFile.exists()) {
System.out.println("檔案不存在!");
return;
}
File desFile = new File(srcFile.getParent(), "copy_" + srcFile.getName());
long partLength = srcFile.length() / n + 1; // 向上取整
for (int i = 1; i <= n; i++) {
long start = partLength * (i - 1);
long end = partLength * i;
new MyThread(srcFile, desFile, start, end).start();
}
}
}
package com.dragon.java.multithreadcopy;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class MyThread extends Thread {
private File srcFile, desFile;
private long start, end;
public MyThread(File srcFile, File desFile, long start, long end) {
this.srcFile = srcFile;
this.desFile = desFile;
this.start = start;
this.end = end;
}
@Override
public void run() {
try (RandomAccessFile rafSrc = new RandomAccessFile(srcFile, "r");
RandomAccessFile rafDes = new RandomAccessFile(desFile, "rw")) {
rafSrc.seek(start);
rafDes.seek(start);
byte[] buffer = new byte[8192]; // 8KB 緩衝區
int len;
while ((len = rafSrc.read(buffer)) != -1) {
rafDes.write(buffer, 0, len);
// 關鍵:檢查是否超出負責區段
if (rafSrc.getFilePointer() >= end) break;
}
System.out.println(getName() + " 複製完成區段 [" + start + ", " + end + ")");
} catch (IOException e) {
System.err.println(getName() + " 異常:" + e.getMessage());
}
}
}
使用 static long pointer 共享進度,每個執行緒一次複製固定長度。缺點:需同步控制指標更新,易產生競爭條件;區段邊界不精確,最後一段可能重複或遺漏。
public class MyThread extends Thread {
private File srcFile, desFile;
private long partLength;
private static long pointer = 0; // 共享指標
public MyThread(File srcFile, File desFile, long partLength) {
this.srcFile = srcFile;
this.desFile = desFile;
this.partLength = partLength;
}
@Override
public void run() {
synchronized (MyThread.class) { // 需同步
long myStart = pointer;
pointer += partLength;
long myEnd = Math.min(pointer, srcFile.length());
try (RandomAccessFile rafSrc = new RandomAccessFile(srcFile, "r");
RandomAccessFile rafDes = new RandomAccessFile(desFile, "rw")) {
rafSrc.seek(myStart);
rafDes.seek(myStart);
byte[] buffer = new byte[(int) Math.min(partLength, myEnd - myStart)];
int len = rafSrc.read(buffer);
rafDes.write(buffer, 0, len);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
| 維度 | 固定區段 (方案一) | 共享指標 (方案二) |
|---|---|---|
| 同步開銷 | 無 (各執行緒獨立區段) | 需 synchronized 鎖 |
| 負載均衡 | 靜態均分,可能不均 | 動態領取,相對均衡 |
| 邊界精確 | 精確控制 | 邊界模糊,需額外處理 |
| 代碼複雜度 | 簡單清晰 | 較複雜 |
| 推薦度 | ⭐⭐⭐⭐⭐ | ⭐⭐ |
transferTo/transferFrom 零拷貝,大檔案更快MappedByteBuffer 將檔案映射至記憶體,極速隨機存取方案一為生產環境推薦做法:簡單、無鎖、邊界可控。方案二僅作為理解共享狀態同步問題的教學範例。