使用Java多線程實現任務分發

多線程下載由來已久,如 FlashGet、NetAnts 等工具,它們都是依懶於 HTTP 協議的支持(Range 字段指定請求內容範圍),首先能讀取出請求內容 (即欲下載的文件) 的大小,劃分出若干區塊,把區塊分段分發給每個線程去下載,線程從本段起始處下載數據及至段尾,多個線程下載的內容最終會寫入到同一個文件中。

    只研究有用的,工作中的需求:要把多個任務分派給Java的多個線程去執行,這其中就會有一個任務列表指派到線程的策略思考:已知:1. 一個待執行的任務列表,2. 指定要啓動的線程數;問題是:每個線程實際要執行哪些任務。

    使用Java多線程實現這種任務分發的策略是:任務列表連續按線程數分段,先保證每線程平均能分配到的任務數,餘下的任務從前至後依次附加到線程中——只是數量上,實際每個線程執行的任務都還是連續的。如果出現那種僧多(線程) 粥(任務) 少的情況,實際啓動的線程數就等於任務數,一挑一。這裏只實現了每個線程各掃自家門前雪,動作快的完成後眼見別的線程再累都是愛莫能助。

    實現及演示代碼如下:由三個類實現,寫在了一個 Java 文件中:TaskDistributor 爲任務分發器,Task 爲待執行的任務,WorkThread 爲自定的工作線程。代碼中運用了命令模式,如若能配以監聽器,用上觀察者模式來控制 UI 顯示就更絕妙不過了,就能實現像下載中的區塊着色跳躍的動感了,在此定義下一步的着眼點了。

    代碼中有較爲詳細的註釋,看這些註釋和執行結果就很容易理解的。main() 是測試方法

package com.alpha.thread;  

  

import java.util.ArrayList;  

import java.util.List;  

  

/** 

 * 指派任務列表給線程的分發器 

 */  

public class TaskDistributor {  

      

    /** 

     * 測試方法 

     * @param args 

     */  

    @SuppressWarnings("unchecked")  

    public static void main(String[] args) {  

        // 初始化要執行的任務列表  

        List taskList = new ArrayList();  

        for (int i = 0; i < 100; i++) {  

            taskList.add(new Task(i));  

        }  

        // 設定要啓動的工作線程數爲 4 個  

        int threadCount = 4;  

        List[] taskListPerThread = distributeTasks(taskList, threadCount);  

        System.out.println("實際要啓動的工作線程數:" + taskListPerThread.length);  

        for (int i = 0; i < taskListPerThread.length; i++) {  

            Thread workThread = new WorkThread(taskListPerThread[i], i);  

            workThread.start();  

        }  

    }  

  

    /** 

     * 把 List 中的任務分配給每個線程,先平均分配,剩於的依次附加給前面的線程 返回的數組有多少個元素 (List) 就表明將啓動多少個工作線程 

     *  

     * @param taskList 

     *            待分派的任務列表 

     * @param threadCount 

     *            線程數 

     * @return 列表的數組,每個元素中存有該線程要執行的任務列表 

     */  

    @SuppressWarnings("unchecked")  

    public static List[] distributeTasks(List taskList, int threadCount) {  

        // 每個線程至少要執行的任務數,假如不爲零則表示每個線程都會分配到任務  

        int minTaskCount = taskList.size() / threadCount;  

        // 平均分配後還剩下的任務數,不爲零則還有任務依個附加到前面的線程中  

        int remainTaskCount = taskList.size() % threadCount;  

        // 實際要啓動的線程數,如果工作線程比任務還多  

        // 自然只需要啓動與任務相同個數的工作線程,一對一的執行  

        // 畢竟不打算實現了線程池,所以用不着預先初始化好休眠的線程  

        int actualThreadCount = minTaskCount > 0 ? threadCount  

                : remainTaskCount;  

        // 要啓動的線程數組,以及每個線程要執行的任務列表  

        List[] taskListPerThread = new List[actualThreadCount];  

        int taskIndex = 0;  

        // 平均分配後多餘任務,每附加給一個線程後的剩餘數,重新聲明與 remainTaskCount  

        // 相同的變量,不然會在執行中改變 remainTaskCount 原有值,產生麻煩  

        int remainIndces = remainTaskCount;  

        for (int i = 0; i < taskListPerThread.length; i++) {  

            taskListPerThread[i] = new ArrayList();  

            // 如果大於零,線程要分配到基本的任務  

            if (minTaskCount > 0) {  

                for (int j = taskIndex; j < minTaskCount + taskIndex; j++) {  

                    taskListPerThread[i].add(taskList.get(j));  

                }  

                taskIndex += minTaskCount;  

            }  

            // 假如還有剩下的,則補一個到這個線程中  

            if (remainIndces > 0) {  

                taskListPerThread[i].add(taskList.get(taskIndex++));  

                remainIndces--;  

            }  

        }  

        // 打印任務的分配情況  

        for (int i = 0; i < taskListPerThread.length; i++) {  

            System.out.println("線程 "  

                    + i  

                    + " 的任務數:"  

                    + taskListPerThread[i].size()  

                    + " 區間["  

                    + ((Task) taskListPerThread[i].get(0)).getTaskId()  

                    + ","  

                    + ((Task) taskListPerThread[i].get(taskListPerThread[i].size() - 1))  

                            .getTaskId() + "]");  

        }  

        return taskListPerThread;  

    }  

}  




package com.alpha.thread;  

  

/** 

 * 要執行的任務,可在執行時改變它的某個狀態或調用它的某個操作 例如任務有三個狀態,就緒,運行,完成,默認爲就緒態 要進一步完善,可爲 Task 

 * 加上狀態變遷的監聽器,因之決定UI的顯示 

 */  

class Task {  

    public static final int READY = 0;  

    public static final int RUNNING = 1;  

    public static final int FINISHED = 2;  

    @SuppressWarnings("unused")  

    private int status;  

    // 聲明一個任務的自有業務含義的變量,用於標識任務  

    private int taskId;  

  

    // 任務的初始化方法  

    public Task(int taskId) {  

        this.status = READY;  

        this.taskId = taskId;  

    }  

  

    /** 

     * 執行任務 

     */  

    public void execute() {  

        // 設置狀態爲運行中  

        setStatus(Task.RUNNING);  

        System.out.println("當前線程 ID 是:" + Thread.currentThread().getName()  

                + " | 任務 ID 是:" + this.taskId);  

        // 附加一個延時  

        try {  

            Thread.sleep(1000);  

        } catch (InterruptedException e) {  

            e.printStackTrace();  

        }  

        // 執行完成,改狀態爲完成  

        setStatus(FINISHED);  

    }  

  

    public void setStatus(int status) {  

        this.status = status;  

    }  

  

    public int getTaskId() {  

        return taskId;  

    }  

}  



package com.alpha.thread;  

  

import java.util.List;  

  

/** 

 * 自定義的工作線程,持有分派給它執行的任務列表 

 */  

class WorkThread extends Thread {  

    // 本線程待執行的任務列表,你也可以指爲任務索引的起始值  

    private List<Task> taskList = null;  

    @SuppressWarnings("unused")  

    private int threadId;  

  

    /** 

     * 構造工作線程,爲其指派任務列表,及命名線程 ID 

     *  

     * @param taskList 

     *            欲執行的任務列表 

     * @param threadId 

     *            線程 ID 

     */  

    @SuppressWarnings("unchecked")  

    public WorkThread(List taskList, int threadId) {  

        this.taskList = taskList;  

        this.threadId = threadId;  

    }  

  

    /** 

     * 執行被指派的所有任務 

     */  

    public void run() {  

        for (Task task : taskList) {  

            task.execute();  

        }  

    }  

}  


執行結果如下,注意觀察每個Java多線程分配到的任務數量及區間。直到所有的線程完成了所分配到的任務後程序結束:

線程 0 的任務數:25 區間[0,24]  

線程 1 的任務數:25 區間[25,49]  

線程 2 的任務數:25 區間[50,74]  

線程 3 的任務數:25 區間[75,99]  

實際要啓動的工作線程數:4  

當前線程 ID 是:Thread-0 | 任務 ID 是:0  

當前線程 ID 是:Thread-3 | 任務 ID 是:75  

當前線程 ID 是:Thread-1 | 任務 ID 是:25  

當前線程 ID 是:Thread-2 | 任務 ID 是:50  

當前線程 ID 是:Thread-1 | 任務 ID 是:26  

當前線程 ID 是:Thread-3 | 任務 ID 是:76  

當前線程 ID 是:Thread-0 | 任務 ID 是:1  

當前線程 ID 是:Thread-2 | 任務 ID 是:51  


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章