首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java RandomAccessFile 詳解:隨機訪問文件實現多線程斷點下載核心原理

     編輯:Java 綜合教學

Java RandomAccessFile 詳解:隨機訪問文件實現多線程斷點下載核心原理

RandomAccessFile 是 Java IO 體系中獨特的存在:它不屬於標準流體系,但封裝了輸入/輸出流,支持隨機讀寫、文件指針定位、權限模式控制。本文通過代碼演示其核心 API,並揭示多線程斷點下載的底層實現原理。

一、核心特性

  • 可視為一個大型 byte[] 數組,支持任意位置讀寫。
  • 構造函數:new RandomAccessFile(File/String name, String mode),mode:"r" (只讀)、"rw" (讀寫)、"rws" (同步寫入)、"rwd" (同步寫入內容)。
  • 關鍵方法:seek(long pos) 移動指針、skipBytes(int n) 相對跳過、getFilePointer() 獲取當前指針、length() 文件長度、read/write 系列。

二、基礎讀寫代碼演示

public class RandomAccessFileDemo {
    public static void main(String[] args) throws Exception {
        // 寫入
        try (RandomAccessFile raf = new RandomAccessFile("test2.txt", "rw")) {
            raf.write("陶士涵李小明".getBytes(StandardCharsets.UTF_8));
        }

        // 讀取:從位置 6 開始(跳過 "陶士涵" 3 個字符 * 2-3 字節)
        readFile();
    }

    public static void readFile() throws Exception {
        try (RandomAccessFile raf = new RandomAccessFile("test2.txt", "rw")) {
            raf.seek(6); // 移動指針到第 6 字節
            byte[] buf = new byte[1024];
            int len = raf.read(buf);
            System.out.println(new String(buf, 0, len, StandardCharsets.UTF_8)); // 輸出: 李小明
        }
    }
}

三、多線程斷點下載核心原理

HTTP 支持 Range: bytes=start-end 請求頭,服務器返回 206 Partial Content。RandomAccessFile 使多線程並發寫入不同區塊成為可能:

// 線程任務
class DownloadTask implements Runnable {
    private final String url;
    private final RandomAccessFile raf;
    private final long startPos;
    private final long endPos;

    public void run() {
        try {
            HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
            conn.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
            try (InputStream in = conn.getInputStream()) {
                raf.seek(startPos); // 關鍵:定位到各自負責的起始位置
                byte[] buf = new byte[8192];
                int len;
                while ((len = in.read(buf)) != -1) {
                    raf.write(buf, 0, len);
                }
            }
        } catch (Exception e) { e.printStackTrace(); }
    }
}

// 主程序
public void multiThreadDownload(String url, String destFile, int threadCount) throws Exception {
    URL u = new URL(url);
    HttpURLConnection conn = (HttpURLConnection) u.openConnection();
    long fileSize = conn.getContentLengthLong();
    conn.disconnect();

    try (RandomAccessFile raf = new RandomAccessFile(destFile, "rw")) {
        raf.setLength(fileSize); // 預分配文件大小
    }

    long blockSize = fileSize / threadCount;
    Thread[] threads = new Thread[threadCount];
    for (int i = 0; i < threadCount; i++) {
        long start = i * blockSize;
        long end = (i == threadCount - 1) ? fileSize - 1 : (i + 1) * blockSize - 1;
        threads[i] = new Thread(new DownloadTask(url, new RandomAccessFile(destFile, "rw"), start, end));
        threads[i].start();
    }
    for (Thread t : threads) t.join();
}

四、關鍵優勢總結

  1. 隨機定位:seek() 讓每個線程精確寫入各自區塊,無需內存緩衝再合併。
  2. 預分配空間:setLength() 避免文件擴展碎片,提升寫入性能。
  3. 斷點續傳:記錄各線程已下載位置,重啟時調用 seek(pos) 續傳。
  4. 同步模式:"rws"/"rwd" 確保關鍵數據(如下載進度元數據)實時落盤。

五、注意事項

  • 非線程安全:多線程操作同一 RandomAccessFile 實例需外部同步(示例中各線程創建獨立實例打開同一文件,依賴 OS 文件鎖)。
  • 大文件內存映射替代:超大文件 (GB 級) 考慮 FileChannel.map() (MappedByteBuffer) 獲得更高性能。
  • 關閉資源:務必在 try-with-resources 或 finally 中關閉,防止文件句柄洩漏。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved