我们知道,单个线程计算是串行的,只有等上一个任务结束之后,才能执行下一个任务,所以执行效率是比较低的。

那么,如果用多线程执行任务,就可以在单位时间内执行更多的任务,而Master-Worker就是多线程并行计算的一种实现方式。

它的思想是,启动两个进程协同工作:Master和Worker进程。

Master负责任务的接收和分配,Worker负责具体的子任务执行。每个Worker执行完任务之后把结果返回给Master,最后由Master汇总结果。(其实也是一种分而治之的思想,和forkjoin计算框架有相似之处,参看:并行任务计算框架forkjoin

Master-Worker工作示意图如下:

下面用Master-Worker实现计算1-100的平方和,思路如下:

  1. 定义一个Task类用于存储每个任务的数据。
  2. Master生产固定个数的Worker,把所有worker存放在workers变量(map)中,Master需要存储所有任务的队列workqueue(ConcurrentLinkedQueue)和所有子任务返回的结果集resultMap(ConcurrentHashMap)。
  3. 每个Worker执行自己的子任务,然后把结果存放在resultMap中。
  4. Master汇总resultMap中的数据,然后返回给Client客户端。
  5. 为了扩展Worker的功能,用一个MyWorker继承Worker重写任务处理的具体方法。

Task类:

package com.thread.masterworker;
public class Task {
private int id;
private String name;
private int num; public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getNum() {
return num;
} public void setNum(int num) {
this.num = num;
}
}

Master实现:

package com.thread.masterworker;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; public class Master {
//所有任务的队列
private ConcurrentLinkedQueue<Task> workerQueue = new ConcurrentLinkedQueue<Task>(); //所有worker
private HashMap<String,Thread> workers = new HashMap<String,Thread>(); //共享变量,worker返回的结果
private ConcurrentHashMap<String,Object> resultMap = new ConcurrentHashMap<String,Object>(); //构造方法,初始化所有worker
public Master(Worker worker,int workerCount){
worker.setWorkerQueue(this.workerQueue);
worker.setResultMap(this.resultMap); for (int i = 0; i < workerCount; i++) {
Thread t = new Thread(worker);
this.workers.put("worker-"+i,t);
}
} //任务的提交
public void submit(Task task){
this.workerQueue.add(task);
} //执行任务
public int execute(){
for (Map.Entry<String, Thread> entry : workers.entrySet()) {
entry.getValue().start();
} //一直循环,直到结果返回
while (true){
if(isComplete()){
return getResult();
}
} } //判断是否所有线程都已经执行完毕
public boolean isComplete(){
for (Map.Entry<String, Thread> entry : workers.entrySet()) {
//只要有任意一个线程没有结束,就返回false
if(entry.getValue().getState() != Thread.State.TERMINATED){
return false;
}
}
return true;
} //处理结果集返回最终结果
public int getResult(){
int res = 0;
for (Map.Entry<String,Object> entry : resultMap.entrySet()) {
res += (Integer) entry.getValue();
}
return res;
} }

父类Worker:

package com.thread.masterworker;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue; public class Worker implements Runnable { private ConcurrentLinkedQueue<Task> workerQueue; private ConcurrentHashMap<String,Object> resultMap; public void setWorkerQueue(ConcurrentLinkedQueue<Task> workerQueue) {
this.workerQueue = workerQueue;
} public void setResultMap(ConcurrentHashMap<String, Object> resultMap) {
this.resultMap = resultMap;
} @Override
public void run() {
while(true){
//从任务队列中取出一个任务
Task task = workerQueue.poll();
if(task == null) break;
//处理具体的任务
Object res = doTask(task);
//把每次处理的结果放到结果集里面,此处直接把num值作为结果
resultMap.put(String.valueOf(task.getId()),res);
} } public Object doTask(Task task) {
return null;
}
}

子类MyWorker继承父类Worker,重写doTask方法实现具体的逻辑:

package com.thread.masterworker;

public class MyWorker extends Worker {
@Override
public Object doTask(Task task) {
//暂停0.5秒,模拟任务处理
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//计算数字的平方
int num = task.getNum();
return num * num;
}
}

客户端Client:

package com.thread.masterworker;

import java.util.Random;

public class Client {
public static void main(String[] args) { Master master = new Master(new MyWorker(), 10); //提交n个任务到任务队列里
for (int i = 0; i < 100; i++) {
Task task = new Task();
task.setId(i);
task.setName("任务"+i);
task.setNum(i+1);
master.submit(task);
} //执行任务
long start = System.currentTimeMillis();
int res = master.execute();
long time = System.currentTimeMillis() - start;
System.out.println("结果:"+res+",耗时:"+time);
}
}

以上,我们用10个线程去执行子任务,最终由Master做计算求和(1-100的平方和)。每个线程暂停500ms,计算数字的平方值。

总共100个任务,分10个线程并行计算,相当于每个线程均分10个任务,一个任务的时间大概为500ms,故10个任务为5000ms,再加上计算平方值的时间,故稍大于5000ms。结果如下,

结果:338350,耗时:5084

并发编程之Master-Worker模式的更多相关文章

  1. Java并发编程之CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  2. python并发编程之asyncio协程(三)

    协程实现了在单线程下的并发,每个协程共享线程的几乎所有的资源,除了协程自己私有的上下文栈:协程的切换属于程序级别的切换,对于操作系统来说是无感知的,因此切换速度更快.开销更小.效率更高,在有多IO操作 ...

  3. 并发编程之J.U.C的第一篇

    并发编程之J.U.C AQS 原理 ReentrantLock 原理 1. 非公平锁实现原理 2)可重入原理 3. 可打断原理 5) 条件变量实现原理 3. 读写锁 3.1 ReentrantRead ...

  4. 并发编程之ThreadLocal

    并发编程之ThreadLocal 前言 当多线程访问共享可变数据时,涉及到线程间同步的问题,并不是所有时候,都要用到共享数据,所以就需要线程封闭出场了. 数据都被封闭在各自的线程之中,就不需要同步,这 ...

  5. [转载]并发编程之Operation Queue和GCD

    并发编程之Operation Queue http://www.cocoachina.com/applenews/devnews/2013/1210/7506.html 随着移动设备的更新换代,移动设 ...

  6. 并发编程之wait()、notify()

    前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程 ...

  7. 并发编程之 Exchanger 源码分析

    前言 JUC 包中除了 CountDownLatch, CyclicBarrier, Semaphore, 还有一个重要的工具,只不过相对而言使用的不多,什么呢? Exchange -- 交换器.用于 ...

  8. 并发编程之 Condition 源码分析

    前言 Condition 是 Lock 的伴侣,至于如何使用,我们之前也写了一些文章来说,例如 使用 ReentrantLock 和 Condition 实现一个阻塞队列,并发编程之 Java 三把锁 ...

  9. python并发编程之Queue线程、进程、协程通信(五)

    单线程.多线程之间.进程之间.协程之间很多时候需要协同完成工作,这个时候它们需要进行通讯.或者说为了解耦,普遍采用Queue,生产消费模式. 系列文章 python并发编程之threading线程(一 ...

  10. python并发编程之gevent协程(四)

    协程的含义就不再提,在py2和py3的早期版本中,python协程的主流实现方法是使用gevent模块.由于协程对于操作系统是无感知的,所以其切换需要程序员自己去完成. 系列文章 python并发编程 ...

随机推荐

  1. 三、Nginx原理解析

    Nginx原理解析 一.反向代理 工作流程 用户通过域名发出访问Web服务器的请求,该域名被DNS服务器解析为反向代理服务器的IP地址: 反向代理服务器接受用户的请求: 反向代理服务器在本地缓存中查找 ...

  2. JVM系列七(JIT 即时编译器).

    一.概述 即时编译器(Just In Time Compiler),也称为 JIT 编译器,它的主要工作是把热点代码编译成与本地平台相关的机器码,并进行各种层次的优化,从而提高代码执行的效率. 那么什 ...

  3. 【python系统学习07】一张图看懂字典并学会操作

    点击跳转 - 原文地址 数据类型 - 字典(dict) 目录: 一张图get字典 字典是什么 js的对象 字典长啥样 语法伪代码 示例demo 语法成像 字典怎么用 字典长度获取--len函数 提取字 ...

  4. [洛谷P3254] [网络流24题] 圆桌游戏

    Description 假设有来自m 个不同单位的代表参加一次国际会议.每个单位的代表数分别为ri (i =1,2,--,m). 会议餐厅共有n 张餐桌,每张餐桌可容纳ci (i =1,2,--,n) ...

  5. 用Kolla在阿里云部署10节点高可用OpenStack

    为展现 Kolla 的真正实力,我在阿里云使用 Ansible 自动创建 10 台虚机,部署一套多节点高可用 OpenStack 集群! 前言 上次 Kolla 已经表示了要打 10 个的愿望,这次我 ...

  6. 超越队西柚考勤系统——beta冲刺3

    一.成员列表 姓名 学号 蔡玉蓝(组长) 201731024205 郑雪 201731024207 何玉姣 201731024209 王春兰 201731024211 二.SCRUM部分 (1)各成员 ...

  7. robotframework,移动端(小程序)自动化,通过屏幕坐标点击对应按钮的方法

    使用场景: 下图通过常规方法是定位不到“红色”这个按钮的 我们把鼠标放置上去,下图右侧会显示该点的坐标地址 然后使用click a point指令定位 click a point 64 743 dur ...

  8. 透过现象看webpack处理css文件中图片路径转换的具体过程

    webpack是目前使用比较流行的一个前端模块打包器,前端的任何资源都被当成一个模块来处理,如图片.css文件等等.在基于webpack构建的前端项目中,一般都会配置有关css文件处理的规则,这其中也 ...

  9. ROS机器人之话题(一)

     将消息发布到话题上 首先建立一个basic包(名称自己定)其命令为: 1.创建功能包,应用catkin_create_pkg命令     cd  ~/catkin_ws/src #切换到代码空间,也 ...

  10. 死磕java(2)

    java数据类型 boolean --true--false byte --8位 short --16位 int --32位 long --64位 char float --32位 double -- ...