Java 併發模式之Master-Worker

RockoZZ發表於2014-05-14

   注:本文摘記於《Java程式效能優化》一書,轉載請註明出處。


    Master-Worker模式的核心思想是:系統有兩類程式協作工作,即Master程式和Worker程式。Master程式負責接收和分配任務,Worker程式負責處理子任務。當各個Worker程式將子任務處理完後,將結果返回給Master程式,由Master程式做歸納和彙總,從而得到系統的最終結果。其處理過程如下圖:


Master-Worker模式的好處,它能夠將一個大任務分解成若干個小任務,並行執行,從而提高系統的吞吐量。而對於系統請求者Client來說,任務一旦提交,Master程式會分配任務並立即返回,並不會等待系統全部處理完成後再返回,其處理過程是非同步的。因此Client不會出現等待現象。



   1.Master-Worker模式結構
    Master-Worker模式的結構相對比較簡單,此處將給出一個簡明的實現方式。如下圖,Master程式為主要程式,它維護了一個Worker程式佇列、子任務佇列和子結果集。Worker程式佇列中的Worker程式,不停地從人物佇列中提取要處理的子任務,並將子任務的處理結構寫入結果集。

 注意:Master-Worker模式是一種使用多執行緒進行資料處理的結構。多個Worker程式協作處理使用者請求,Master程式負責維護Worker程式,並整合最終處理結果。
Master-Worker模式的主要參與者如下圖表4.2所示:



    2.Master-Worker的程式碼實現
    基於以上所述的設計思路,這裡給出一個簡易的Master-Worker框架。其中Master部分的實現程式碼如下:

package com.roc.masterworker;

import java.util.HashMap;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public class Master
{
	/**
	 * 任務佇列
	 */
	protected Queue<Object> workQueue = new ConcurrentLinkedQueue<Object>();
	/**
	 * Worker程式佇列
	 */
	protected Map<String, Thread> threadMap = new HashMap<String, Thread>();
	/**
	 * 子任務處理結果集
	 */
	protected Map<String, Object> resultMap = new ConcurrentHashMap<String, Object>();

	/**
	 * Master的構造,需要一個Worker程式邏輯,和需要的Worker程式數量
	 */
	public Master(Worker worker, int countWorker)
	{
		worker.setWorkQueue(workQueue);
		worker.setResultMap(resultMap);
		for (int i = 0; i < countWorker; i++)
		{
			threadMap.put(Integer.toString(i), new Thread(worker, Integer.toString(i)));
		}
	}

	/**
	 * 提交一個任務
	 */
	public void submit(Object job)
	{
		workQueue.add(job);
	}

	/**
	 * 返回子任務結果集
	 */
	public Map<String, Object> getResultMap()
	{
		return resultMap;
	}
	
	/**
	 * 開始執行所有的Worker程式進行處理
	 */
	public void execute()
	{
		for(Map.Entry<String, Thread> entry :threadMap.entrySet())
		{
			entry.getValue().start();
		}
	}

	/**
	 * 是否所有的子任務都結束了
	 */
	public boolean isComplete()
	{
		for (Map.Entry<String, Thread> entry : threadMap.entrySet())
		{
			if (entry.getValue().getState() != Thread.State.TERMINATED)
			{
				return false;
			}
		}
		return true;
	}
}


    對應的Worker程式實現如下:
package com.roc.masterworker;

import java.util.Map;
import java.util.Queue;

public class Worker implements Runnable
{
	/**
	 * 任務佇列,用於取得子任務
	 */
	protected Queue<Object> workerQueue;
	/**
	 * 子任務處理結果集
	 */
	protected Map<String, Object> resultMap;

	public void setResultMap(Map<String, Object> resultMap)
	{
		this.resultMap = resultMap;
	}

	public void setWorkQueue(Queue<Object> workeQueue)
	{
		this.workerQueue = workeQueue;
	}

	/**
	 * 子任務的處理邏輯,在子類中實現具體邏輯
	 */
	public Object handle(Object input)
	{
		return input;
	}

	@Override
	public void run()
	{
		while (true)
		{
			// 獲取子任務
			Object input = workerQueue.poll();
			if (input == null)
			{
				break;
			}
			// 處理子任務
			Object result = handle(input);
			// 將處理結果寫入結果集
			resultMap.put(Integer.toString(input.hashCode()), result);
		}
	}

}

    以上兩段程式碼已經展示了Master-Worker的框架全貌。應用程式中通過過載Worker.handle()方法實現應用層邏輯。
    注意:Master-Worker模式是一種將序列化任務並行化的方法,被分解的子任務在系統中可以被並行處理。同時,如果有需要,Master程式不需要等待所有子任務都完成計算,就可以根據已有的部分結果集計算最終結果。



    3.應用舉例
    現應用這個Master-Worker框架,實現一個計算立方體和的應用並計算1~100的立方體和。即13+23+······+1003。任務分解圖如圖4.7所示。
    計算任務被分解為100個子任務,每個子任務僅用於計算單獨的立方和。Master產生固定個數的Worker,來處理所有這些子任務。Worker不斷地從任務集合中取得這些計算立方和的子任務,並將計算結果返回給Master。Master負責將所有Worker的任務結果進行累加,從而產生最終的立方和。在整個計算過程中,Master與Worker的執行也是完全非同步的,Master不必等到所有的Worker都執行完後,就可以進行求和操作,即,Master在獲得部分子任務結果集時,就已經可以開始對最終結果進行計算,從而進一步提高系統的併發度和吞吐量。


    Worker物件在應用層的程式碼實現如下:

package com.roc.masterworker.test;

import java.util.Map;
import java.util.Set;

import com.roc.masterworker.Master;

public class Test
{
	public static void main(String[] args)
	{
		// 使用5個Worker,並指定Worker
		Master m = new Master(new PlusWorker(), 5);
		// 提交100個子任務
		for (int i = 0; i < 100; i++)
		{
			m.submit(i);
		}
		// 開始計算
		m.execute();
		// 最終計算結果儲存於此
		int result = 0;

		Map<String, Object> resultMap = m.getResultMap();

		String key = null;
		Integer i = null;
		Set<String> keys = null;
		while (resultMap.size() > 0 || !m.isComplete())
		{
			// 不需要等待所有Worker都執行完,即可開始計算最終結果
			keys = resultMap.keySet();
			for (String k : keys)
			{
				key = k;
				break;
			}
			if (key != null)
			{
				i = (Integer) resultMap.get(key);
			}
			if (i != null)
			{
				// 最終結果
				result += i;
			}
			if (key != null)
			{
				// 移除已經被計算過得項
				resultMap.remove(key);
			}
		}
		System.out.println("-->result:" + result);;
	}
}

    在主函式中,首先通過Master類建立5個Worker工作程式和Worker工作例項PlusWorker。在提交了100個子任務後,便開始子任務的計算。這些子任務,由生成的5個Worker程式共同完成。Master並不等待所有的Worker執行完畢,就開始訪問子結果集進行最終結果的計算,直到子結果集中所有的資料都被處理,並且5個活躍的Worker程式全部終止,才給出最終計算結果。

    原始碼下載地址:名字為Master-Worker


相關文章