Java多线程之任务分发

题引:

老师有3个同类任务,需要5个同学ABCDE分别都要处理完成。老师分配任务的策略是:每个任务都是随机分配下去的,每个同学拿到任务后都要完成这个任务。

像极了做数学题时的套路,这个题目其实就属于Java多线程之任务分发的模式。

在实际工作中,如果我们遇到了“要把多个同类任务分派给Java的多个线程去执行”的场景,需要思考如下的问题:

(1)待执行的任务列表是什么?
(2)需要启动多少Java线程去执行任务?
(3)每个Java线程实际要执行多少任务?(or:任务如何分发给每个Java线程?)

思考之后,我们还需要知道,Java多线程实现这种任务分发的一般策略是什么,为了便于下文叙述方便,我们先约定:

(1)任务数为taskNum
(2)计划启动线程数为planThreadNum
(3)实际启动线程数为realThreadNum

(1)确认实际启动的线程数:

如果taskNum < planThreadNum,则实际启动taskNum个线程,即一个线程执行一个任务;

如果taskNum众多,则实际启动planThreadNum个线程即可。

(2)确定每个线程执行的任务数:

如果taskNum < planThreadNum,每个线程都只执行一个任务;

如果taskNum众多,则先将taskNum按planThreadNum均分,余下的任务从前至后依次附加到线程中即可。

现在,我们既知道了多任务分发该思考哪些问题,也明白了Java多线程处理任务分发的一般策略,该如何解决题引中的问题呢?由题设和上文中的策略可知:

(1)我们需要一个Task类,来定义任务的状态(就绪、执行中、结束)和任务的行为(执行)
(2)我们需要一个Teacher类,来执行任务分配的工作
(3)我们需要一个StudentWorkThread类,来定义学生的工作线程,由该线程来执行任务
(4)我们需要一个DoTeacherWork的测试类,用来测试题引中的场景

(1)Task类:

public class Task {

    private static final int READY = 0;
    private static final int RUNNING = 1;
    private static final int FINISHED = 2;

    // 执行任务的状态
    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(Task.FINISHED);
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public int getTaskId() {
        return taskId;
    }

}

(2)Teacher类:

public class Teacher {

    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[] perThreadWorkList = new List[actualThreadCount];
        int taskIndex = 0;
        // 任务剩余数重新声明标志,防止在执行过程中改变剩余线程数remainTaskCount的值
        int remainIndces = remainTaskCount;

        for (int i = 0; i < perThreadWorkList.length; i++) {
            perThreadWorkList[i] = new ArrayList();
            // 如果每个线程至少要执行的任务数>0,线程需要分配到基本任务
            if (minTaskCount > 0) {
                for (int j = taskIndex; j < minTaskCount + taskIndex; j++) {
                    perThreadWorkList[i].add(taskList.get(j));
                }
            }
            taskIndex += minTaskCount;

            // 还有剩余的任务,则补充一个到这个线程中
            if (remainIndces > 0) {
                perThreadWorkList[i].add(taskList.get(taskIndex++));
                remainIndces--;
            }
        }

        // 打印任务的分配情况
        for (int i = 0; i < perThreadWorkList.length; i++) {
            System.out.println("线程" + i + "任务数: " + perThreadWorkList[i].size() +
                    ",区间[" + ((Task) perThreadWorkList[i].get(0)).getTaskId() + "," +
                    ((Task) perThreadWorkList[i].get(perThreadWorkList[i].size() - 1)).getTaskId() + "]");
        }

        return perThreadWorkList;
    }
    
}

(3)StudentWorkThread类:

public class StudentWorkThread extends Thread {

    private List<Task> taskList = null;
    private int threadId;

    public StudentWorkThread(List<Task> taskList, int threadId) {
        this.taskList = taskList;
        this.threadId = threadId;
    }

    // 执行被指派的任务
    public void run() {
        for (Task task : taskList) {
            task.execute();
        }
    }

}

(4)DoTeacherWork类:

public class DoTeacherWork {

    public static void main(String[] args) {
        // 构建老师的任务列表
        List<Task> taskList = new ArrayList<Task>();
        for (int i = 0; i < 3; i++) {
            taskList.add(new Task(i));
        }

        System.out.println("老师的任务列表数:" + taskList.size());

        // 设定学生工作的线程数为5
        int threadNum = 5;

        List[] perThreadWorkList = Teacher.distributeTasks(taskList, threadNum);

        System.out.println("实际要启动的工作线程数:" + perThreadWorkList.length);

        for (int i = 0; i < perThreadWorkList.length; i++) {
            StudentWorkThread studentWorkThread = new StudentWorkThread(perThreadWorkList[i], i);
            studentWorkThread.start();
        }
    }

}

结束语:题引中的任务分发只是一个简单的Java多线程应用场景,在实际工作中,在每一个思考环节的处理上,都可以进行扩展和细化需求。在此就不进行阐述了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,074评论 25 707
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,240评论 11 349
  • 文/孤鸟差鱼 水壶里蹦出一只姑娘 她干瘪的模样 让我怀疑水壶 坏了 水壶姑娘看出了我的疑虑 说自己一直待在内胆里 ...
    孤鸟差鱼阅读 322评论 5 7
  • 雨,滴答,滴答……让街头的人们奔走回家,雨声掩盖了吵闹。故事在这个小镇开始上演…… 南洋十一岁读初中,好孩子一枚,...
    老王我要去武汉阅读 448评论 2 2