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

Java 多線程下載實戰:斷點續傳核心原理與代碼實現

     編輯:Java 程式設計入門

多線程下載利用 HTTP/1.1 的 Range 請求頭實現分塊並行傳輸,配合 RandomAccessFile 的隨機讀寫能力,可顯著提升大文件下載速度並支持斷點續傳。本文通過完整代碼示例,講解核心步驟與關鍵細節。

核心原理三步走

  1. 預建文件佔位:獲取 Content-Length,用 RandomAccessFile.setLength() 在本地創建同等大小空文件。
  2. 計算分塊範圍:將文件長度按線程數均分,餘數歸最後一線程處理。
  3. 分段請求與寫入:各線程發送 Range: bytes=start-end,響應碼 206 Partial Content 表示成功,通過 seek(start) 定位寫入。

完整實現代碼

package cn.me.test;

import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;

public class MultiThreadDownload {
    public static void main(String[] args) throws Exception {
        // 1. 目標文件與下載地址
        String fileName = "aa.rar";
        String urlStr = "https://localhost:7777/day18";

        // 2. 建立連接獲取文件長度
        URL url = new URL(urlStr + "/" + fileName);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("GET");
        int length = con.getContentLength(); // 關鍵:獲取總字節數

        // 3. 預創建同等大小文件佔位
        RandomAccessFile file = new RandomAccessFile("d:/a/" + fileName, "rw");
        file.setLength(length);
        file.close();

        // 4. 線程數與分塊計算
        int threadCount = 3;
        int block = length % threadCount == 0 ? length / threadCount : length / threadCount + 1;
        System.err.println("每個線程下載:" + block + " 字節");

        // 5. 啟動線程
        for (int i = 0; i < threadCount; i++) {
            int start = i * block;
            int end = Math.min(start + block - 1, length - 1); // 修正邊界
            System.err.println("線程 " + i + ": " + start + " - " + end);
            new DownloadThread(fileName, start, end, url).start();
        }
    }

    static class DownloadThread extends Thread {
        private final String fileName;
        private final int start;
        private final int end;
        private final URL url;

        public DownloadThread(String fileName, int start, int end, URL url) {
            this.fileName = fileName;
            this.start = start;
            this.end = end;
            this.url = url;
        }

        @Override
        public void run() {
            try {
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setRequestMethod("GET");
                // 關鍵:設置分段範圍
                conn.setRequestProperty("Range", "bytes=" + start + "-" + end);

                if (conn.getResponseCode() == 206) { // 206 = Partial Content
                    try (InputStream in = conn.getInputStream();
                         RandomAccessFile out = new RandomAccessFile("d:/a/" + fileName, "rw")) {

                        out.seek(start); // 定位寫入位置
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = in.read(buffer)) != -1) {
                            out.write(buffer, 0, len);
                        }
                    }
                }
                System.err.println(getName() + " 完成");
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

關鍵點深度解析

1. RandomAccessFile 的 "rw" 模式

允許在文件任意位置讀寫,seek(pos) 移動文件指針,是分塊寫入的基石。

2. Range 請求頭細節

  • bytes=0-499:前 500 字節
  • bytes=500-:第 500 字節到結尾(斷點續傳常用)
  • 服務器必須支持 Accept-Ranges: bytes 並返回 206 狀態碼

3. 邊界計算修正

原代碼 end = start + block - 1 可能導致最後一線程越界,建議用 Math.min(start + block - 1, length - 1)。

4. 資源管理最佳實踐

使用 try-with-resources 自動關閉流,避免洩漏;生產環境應加入進度持久化、校驗和重試機制。

進階擴展方向

  • 斷點續傳:將各線程 start/end/currentPos 序列化保存,恢復時讀取繼續。
  • 動態線程池:根據網絡狀況調整並發數。
  • 校驗機制:下載完成後驗證 MD5/SHA-256。

掌握多線程下載原理,不僅能實現高效下載器,更能深入理解 HTTP 協議、NIO 與並發編程的結合應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved