多線程系列之學習多線程下載的基本原理和基本用法(1)

多線程下載在咱們生活中很是常見,好比迅雷就是咱們經常使用的多線程的下載工具,固然還有斷點續傳,斷點續傳咱們在下一節來說,android手機端下載文件時也能夠用多線程下載,咱們這裏是在java中寫一個測試,其實android中的實現和java是同樣的,學會了java就知道怎麼在android中實現了,廢話很少說了,怎麼實現多線程和多線程的原理是什麼樣的,如今咱們來學習一下。
<!--more-->java

多線程下載原理及步驟:

  1. 在本地建立一個大小跟服務器文件相同大小的臨時文件。android

  2. 計算分配幾個線程去下載服務器上的資源,知道每一個線程下載文件的位置。程序員

步驟二的具體方法和操做:

文件的長度/3(線程的個數)=每一個線程下載文件的大小服務器

假設文件長度爲10,則網絡

線程1:0-2多線程

線程2:3-5dom

線程3:6-文件末尾ide

每一個線程下載的位置的計算方式:工具

開始位置:學習

(線程id - 1)* 每一塊的大小

結束位置:

(線程id * 每一塊大小)-1

  1. 開啓多(3)個線程,每個線程下載對應位置的文件

  2. 若是全部的線程都把本身的數據下載完畢了,服務器上的資源就被下載到本地了。

在這裏在介紹一個有關多線程下載的java中的相關類RandomAccessFile

RandomAccessFile 隨機文件訪問類

只有RandomAccessFile纔有seek搜尋方法,而這個方法也只適用於文件。經過seek()方法指定位置,定位文件,便可以指定隨機寫文件的時候從哪一個位置開始寫。利用這個類才能實現文件的多線程下載。

基本原理和相關介紹如上,就這些,如今咱們看看代碼:

package net.loonggg.test;

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

/***
 * 多線程下載
 * 
 * @author loonggg
 * 
 */
public class MutilDownloader {
    // 開啓的線程的個數
    public static final int THREAD_COUNT = 3;

    public static void main(String[] args) throws Exception {
        String path = "http://down.360safe.com/yunpan/360wangpan_setup.exe";
        // 鏈接服務器,獲取一個文件,獲取文件的長度,在本地建立一個大小跟服務器文件大小同樣的臨時文件
        URL url = new URL(path);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        // 設置網絡請求超時時間
        conn.setConnectTimeout(5000);
        // 設置請求方式
        conn.setRequestMethod("GET");
        int code = conn.getResponseCode();
        if (code == 200) {
            // 服務器返回的數據的長度,實際就是文件的長度
            int length = conn.getContentLength();
            System.out.println("----文件總長度----" + length);
            // 在客戶端本地建立出來一個大小跟服務器端文件同樣大小的臨時文件
            RandomAccessFile raf = new RandomAccessFile("yunpan.exe", "rwd");
            // 指定建立的這個文件的長度
            raf.setLength(length);
            // 關閉raf
            raf.close();
            // 假設是3個線程去下載資源
            // 平均每個線程下載的文件的大小
            int blockSize = length / THREAD_COUNT;
            for (int threadId = 1; threadId <= THREAD_COUNT; threadId++) {
                // 計算每一個線程下載的開始位置和結束位置
                int startIndex = (threadId - 1) * blockSize;
                int endIndex = threadId * blockSize - 1;
                if (threadId == THREAD_COUNT) {
                    endIndex = length;
                }
                System.out.println("----threadId---" + threadId
                        + "--startIndex--" + startIndex + "--endIndex--"
                        + endIndex);
                // 開啓每個線程
                new DownloadThread(path, threadId, startIndex, endIndex)
                        .start();
            }
        }

    }

    /**
     * 下載文件的子線程,每個線程下載對應位置的文件
     * 
     * @author loonggg
     * 
     */
    public static class DownloadThread extends Thread {
        private int threadId;
        private int startIndex;
        private int endIndex;
        private String path;

        /**
         * @param path
         *            下載文件在服務器上的路徑
         * @param threadId
         *            線程id
         * @param startIndex
         *            線程下載的開始位置
         * @param endIndex
         *            線程下載的結束位置
         */
        public DownloadThread(String path, int threadId, int startIndex,
                int endIndex) {
            this.path = path;
            this.threadId = threadId;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        @Override
        public void run() {
            try {
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url
                        .openConnection();
                conn.setRequestMethod("GET");
                // 重要:請求服務器下載部分的文件 指定文件的位置
                conn.setRequestProperty("Range", "bytes=" + startIndex + "-"
                        + endIndex);
                conn.setConnectTimeout(5000);
                // 從服務器請求所有資源的狀態碼200 ok 若是從服務器請求部分資源的狀態碼206 ok
                int code = conn.getResponseCode();
                System.out.println("---code---" + code);
                InputStream is = conn.getInputStream();// 已經設置了請求的位置,返回的是當前位置對應的文件的輸入流
                RandomAccessFile raf = new RandomAccessFile("yunpan.exe", "rwd");
                // 隨機寫文件的時候從哪一個位置開始寫
                raf.seek(startIndex);// 定位文件
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer)) != -1) {
                    raf.write(buffer, 0, len);
                }
                is.close();
                raf.close();
                System.out.println("線程" + threadId + ":下載完畢了!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

公衆號:非著名程序員(smart_android) 歡迎你們關注,天天一篇原創技術文章。

公衆號:非著名程序員(smart_android) 歡迎你們關注,天天一篇原創技術文章。

相關文章
相關標籤/搜索