并发编程之Master-Worker模式

来源:互联网 发布:信息化校园大数据引领 编辑:程序博客网 时间:2024/06/05 07:50

Master-Worker模式的核心思想是在于Master进程和Worker进程各自分担各自的任务,协同完成信息处理的模式
使用一个图例来简单描述一下:
Master-Worker工作原理
如图所示Master-Worker的工作机制就是Master接收到了一个任务,对任务进行拆分,并且分配给各个Worker,让各个Worker之间各自运行各自的子任务,最后Worker们的返回结果交给Master进行汇总并且最后返回给任务的发起方.

Master-Worker是一种并行模式,Master是主要进程,Master中有维护着一个Worker进程队列.Master把一个大的而且复杂的业务拆分成若干小的业务,只要是互不影响的都可以分而治之相互独立.可以通过多线程或多进程甚至多机联合计算,把拆分后的小业务交给更多的CPU或机器处理,通过并发/并行的方式提高整体业务的运算速度,压榨系统性能来提高效率.

这样做的好处就是,在某些业务场景下,尤其是业务比较复杂而且数据量较大的情况下,例如财务账单结算和生成.一个账单需要有很多关联计算而且条件和参数众多的时候,如果把所有的业务都放在一个任务中,效率是比较低的,数据量大的情况下往往耗时巨大,少则几十分钟,夸张的有整整一宿.这个在圆通速递的罗汉财务系统中体现尤其严重..不信可以问问许双芳.

具体的实现细节是Master任务切分 –> 交给任务队列 –> Worker处理任务
Master可以创建Worker线程池,分发任务给Worker.也可以只负责任务的接收和拆分,单不负责Worker的管理,通过其他第三方工具来负责Worker的监控和调度,这样对于解耦方面比较有利.

OK 有了思想之后 剩下的就是通过代码来简单实现一下

先创建Master和Worker
Master:

package com.unsc.Master_Worker;import java.util.HashMap;import java.util.Map;import java.util.Queue;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ConcurrentLinkedQueue;/** * Created by DELL on 2017/12/25. * Master-Worker中的 Master 类 * @author 犯罪嫌疑人卢某 */public class cMaster {    /**     * 创建一个ConcurrentLinkedQueue 用来盛放任务     * ConcurrentLinkedQueue是一个线程安全的无界线程安全队列     * 对元素的排序遵循先进先出的原则 获取元素时返回头部元素 添加元素则为尾部     */    private Queue<Object> workersQueue = new ConcurrentLinkedQueue<>();    /**     * 创建HashMap<K,V> 来存放Worker对象     */    private Map<String, Thread> workers = new HashMap<>();    /**     * 创建ConcurrentHashMap来存放Worker计算后的结果集     */    private Map<String, Object> resultMap = new ConcurrentHashMap<>();    /**     * Master类的有参构造     * @param worker Worker对象     * @param workersCount Worker的数量 用来创建对应数量的线程     */    public cMaster(cWorker worker , int workersCount ) {        worker.setIntoWorkersQueue(this.workersQueue);        worker.setResultMap(this.resultMap);        /*         * 创建对应数量的线程来模拟Worker         * 至于显式new Thread() 阿里的代码规约什么的对我这个无业游民毫无约束 随它去         */        for (int i = 0 ; i < workersCount ; i ++) {            workers.put(Integer.toString(i) , new Thread(worker , Integer.toString(i)));        }    }    /**     * 判断是否所有的子任务都已完成     * @return 是否完成     */    public boolean isComplete() {        for(Map.Entry<String, Thread> entry : workers.entrySet()) {            if (entry.getValue().getState() != Thread.State.TERMINATED) {                return false;            }        }        return true;    }    /**     * 向任务队列中提交子任务     */    public void missionSubmit(Object mission) {        workersQueue.add(mission);    }    /**     * 返回子任务的结果集     * @return 子任务运行完毕的结果集     */    public Map<String, Object> getResultMap() {        return resultMap;    }    /**     * 启动所有的Workers线程 开始并行计算     */    public void startAllWorkes() {        for (Map.Entry<String,Thread> threadEntry : workers.entrySet()) {            threadEntry.getValue().start();        }    }}

再是Worker:

package com.unsc.Master_Worker;import java.util.Map;import java.util.Queue;/** * Created by DELL on 2017/12/25. * Master-Worker 中的 Worker类 * @author 犯罪嫌疑人卢某 */public class cWorker implements Runnable{    /**     * Worker中的任务队列     */    private Queue<Object> workQueue;    /**     * Worker中任务的结果集Map     */    private Map<String, Object> resultMap;    /**     * 实现Runnable接口重写的Run方法     */    @Override    public void run() {        /*         * 设置轮询 获取子任务并且处理         */        while (true) {            Object mission = workQueue.poll();            if (mission == null) {                break;            }            /*             * 模拟子任务的处理 并且把处理结果加入结果集             */            Object result = executeMission(mission);            resultMap.put(Integer.toString(mission.hashCode()) , result);        }    }    /**     * 具体执行任务的业务逻辑 这里是基本的Worker 具体的逻辑交给子类实现     * @param mission 任务     * @return 执行结果     */    public Object executeMission(Object mission) {        return mission;    }    /**     * 设置Worker的任务队列     * @param workersQueue 任务队列     */    void setIntoWorkersQueue(Queue<Object> workersQueue) {        this.workQueue = workersQueue;    }    /**     * 设置Worker的结果集     * @param resultMap 结果集     */    void setResultMap(Map<String, Object> resultMap) {        this.resultMap = resultMap;    }}

再创建一个Worker的具体的实现类 这里通过一个TrueWorker类重写Worker中的executeMission方法执行具体的操作.
TrueWorker:

package com.unsc.Master_Worker;import java.util.Map;import java.util.Set;/** * Created by DELL on 2017/12/25. * Master-Worker模式的测试 * @author 犯罪嫌疑人卢某 */public class MasterWorkerTest {    public static void main(String[] args) {        /*         * 设置6个worker和100个子任务 for循环里面的魔法值无视吧         */        cMaster master = new cMaster(new TrueWorker(), 4);        for (int i = 1 ; i < 101 ; i++) {            master.missionSubmit(i + 0.1);        }        /*         * Master让所有的Worker开始工作 并且在运作完毕后获得结果集         */        //打一个时间戳 对比测试消耗的时间        long startTime = System.currentTimeMillis();        master.startAllWorkes();        Map<String, Object> resultMap = master.getResultMap();        long endTime = System.currentTimeMillis();        double finalScore = 0;        while (true) {            Set<String> keySet = resultMap.keySet();            String key = null;            for (String s : keySet) {                key = s;                break;            }            //计算结果变量            Double score = null;            if (key != null) {                //计算结果并且从结果集中删除                score = (Double) resultMap.get(key);                resultMap.remove(key);            }            if (score != null) {                finalScore += score;            }            if (master.isComplete() && resultMap.size() == 0) {                System.out.println("任务执行完毕..");                break;            }        }        System.out.println("结果的值是 --->>> " + finalScore);        System.out.println("总计耗时 : " + (endTime - startTime) + " ms");    }}

因为int类型计算太快了 我才用双精度浮点来运算 排除掉创建线程和其他的操作单纯就关注任务提交和获得结果集的耗时 得到的运行结果如图:
MW方式

同样的再用传统的方法 再来检测一下效率:
代码如下 :

package com.unsc.Master_Worker;/** * Created by DELL on 2017/12/25. * 直接单线程在main中计算 1 到 100的平方和 * @author 犯罪嫌疑人卢某 */public class OldMethod {    public static void main(String[] args) {        /*         * 开始的时间戳         */        long startTime = System.currentTimeMillis();        double finalScore = 0;        for(int i = 0 ; i < 101 ; i++) {            double num = i + 0.1;            finalScore += num * num;        }        System.out.println("结果是 : ---->>>> " + finalScore);        long endTime = System.currentTimeMillis();        System.out.println("耗时总计 : " + (endTime - startTime) + " ms");    }}

结果如图:
传统方式

这里存在一些性能差别 虽然很小 只是1ms的差距 但是在高并发大数据量的情况下 会有比较明显的性能差距 只是单机上进行测试不是很明显罢了..

阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝卖家在哪拿货 批发网站大全拿货 鸭脖子去哪里拿货 服装批发怎么拿货 三草两木怎么拿货 护肤品去哪里拿货 开网店在哪里拿货 化妆品在哪里拿货 卖衣服从哪里拿货 开网店到哪里拿货 化妆品去哪里拿货 化妆品到哪里拿货 开服装店哪里拿货 初次去批发衣服怎么拿货 广州服装批发网上拿货 2018医采各级代理拿货价格表 做童泰代理几折拿货 化妆品批发市场怎么拿货 淘宝卖家在哪里拿货 怎样从厂家直接拿货 衣服批发厂家直销拿货 在淘宝卖东西怎么拿货 百雀羚代理几折拿货 品牌服装折扣批发拿货 巴拉巴拉童装几折拿货 摆地摊一般在哪里拿货 2018杭州四季青散客怎么拿货 拿走 拿走了什么 拿走拼音 他拿起一颗草莓缓缓推入 拿起放下 拿起一颗葡萄缓缓推入 拿得起放得下 拿起一珠子缓缓推入 拿起拼音 他拿起冰块缓缓推进太一 拿得起放得下经典句子 拿起的拼音 拿起一颗葡萄缓缓推入黑花 拿起的勇气