MasterThread:
  1. 持有一个BlockingQueue队列,用于并发接收存储MetaData对象;
  2. 使用Hash一致性算法ketama,来选择SlaveThread节点;
  3. 从BlockingQueue队列中,取出MetaData对象,分配给各SlaveThread节点;
  4. SlaveThread节点负责真正处理MetaData对象;
     
 SlaveThread:
  1. 持有一个BlockingQueue队列,用于存储MetaData对象;
  2. 负责真正处理MetaData对象;

Ketama:
 一致性Hash算法,是一种分布式算法,常用于负载均衡;

测试说明:
  1. 假设处理每个MetaData对象需要耗时0.5秒,需要处理500个MetaData对象;
  2. 若是串行处理,则需要0.5*500=250秒;
  3. 使用上图所示的master/slave算法,则可以5秒内完成;




MasterThread.java

核心程序介绍


SlaveThread.java

核心程序

测试

执行结果:
省略......
省略......



完整程序

HashAlgorithm.java
  1. package ll.concurrent.BlockingQueue.Ketama;
  2. import java.io.UnsupportedEncodingException;
  3. import java.security.MessageDigest;
  4. import java.security.NoSuchAlgorithmException;
  5. public enum HashAlgorithm {
  6. KETAMA_HASH;
  7. private static final String UTF_8 = "UTF-8";
  8. private static final String MD5 = "MD5";
  9. public long hash(byte[] digest, int nTime) {
  10. long rv = ((long) (digest[3 + nTime * 4] & 0xFF) << 24)
  11. | ((long) (digest[2 + nTime * 4] & 0xFF) << 16)
  12. | ((long) (digest[1 + nTime * 4] & 0xFF) << 8)
  13. | (digest[0 + nTime * 4] & 0xFF);
  14. return rv & 0xffffffffL; /* Truncate to 32-bits */
  15. }
  16. public byte[] computeMd5(String key) {
  17. MessageDigest md5;
  18. try {
  19. md5 = MessageDigest.getInstance(MD5);
  20. } catch (NoSuchAlgorithmException e) {
  21. throw new RuntimeException("MD5 not supported", e);
  22. }
  23. md5.reset();
  24. byte[] keyBytes = null;
  25. try {
  26. keyBytes = key.getBytes(UTF_8);
  27. } catch (UnsupportedEncodingException e) {
  28. throw new RuntimeException("Unknown string :" + key, e);
  29. }
  30. md5.update(keyBytes);
  31. return md5.digest();
  32. }
  33. }

KetamaNodeLocator.java
  1. package ll.concurrent.BlockingQueue.Ketama;
  2. import java.util.Collection;
  3. import java.util.TreeMap;
  4. /**
  5. * 一致性Hash算法:是一种分布式算法,常用于负载均衡;
  6. *
  7. * @param <T>
  8. */
  9. public final class KetamaNodeLocator<T> {
  10. private TreeMap<Long, T> ketamaNodes;
  11. private HashAlgorithm hashAlg;
  12. private int numReps = 160;
  13. public KetamaNodeLocator(Collection<T> nodes) {
  14. this(nodes, HashAlgorithm.KETAMA_HASH);
  15. }
  16. public KetamaNodeLocator(Collection<T> nodes, HashAlgorithm alg) {
  17. this(nodes, HashAlgorithm.KETAMA_HASH, 160);
  18. }
  19. public KetamaNodeLocator(Collection<T> nodes, HashAlgorithm alg,
  20. int nodeCopies) {
  21. hashAlg = alg;
  22. ketamaNodes = new TreeMap<Long, T>();
  23. numReps = nodeCopies;
  24. for (T node : nodes) {
  25. for (int i = 0; i < numReps / 4; i++) {
  26. byte[] digest = hashAlg.computeMd5(node.toString() + i);
  27. for (int h = 0; h < 4; h++) {
  28. long m = hashAlg.hash(digest, h);
  29. ketamaNodes.put(m, node);
  30. }
  31. }
  32. }
  33. }
  34. public T getNode(final String k) {
  35. byte[] digest = hashAlg.computeMd5(k);
  36. T rv = getNodeForKey(hashAlg.hash(digest, 0));
  37. return rv;
  38. }
  39. T getNodeForKey(long hash) {
  40. if (ketamaNodes.isEmpty()) {
  41. return null;
  42. }
  43. if (!ketamaNodes.containsKey(hash)) {
  44. Object ceilValue = ((TreeMap<Long, T>) ketamaNodes)
  45. .ceilingKey(hash);
  46. if (ceilValue != null) {
  47. try {
  48. hash = Long.valueOf(ceilValue.toString());
  49. } catch (NumberFormatException e) {
  50. e.printStackTrace();
  51. hash = 0;
  52. }
  53. }
  54. if (ceilValue == null || hash == 0) {
  55. hash = ketamaNodes.firstKey();
  56. }
  57. }
  58. return ketamaNodes.get(hash);
  59. }
  60. }

MasterThread.java
  1. package ll.concurrent.BlockingQueue;
  2. import java.util.HashMap;
  3. import java.util.Map;
  4. import java.util.concurrent.BlockingQueue;
  5. import java.util.concurrent.ExecutorService;
  6. import java.util.concurrent.Executors;
  7. import java.util.concurrent.LinkedBlockingQueue;
  8. import ll.concurrent.BlockingQueue.Ketama.KetamaNodeLocator;
  9. /**
  10. * <pre>
  11. * MasterThread:
  12. * 1. 持有一个BlockingQueue队列,用于并发接收存储MetaData对象;
  13. * 2. 使用Hash一致性算法ketama来选择SlaveThread节点;
  14. * 3. 从BlockingQueue队列中,取出MetaData对象,分配给SlaveThread节点;
  15. * 4. SlaveThread节点负责真正处理MetaData对象;
  16. *
  17. * SlaveThread:
  18. * 1. 持有一个BlockingQueue队列,用于存储MetaData对象;
  19. * 2. 负责真正处理MetaData对象;
  20. * </pre>
  21. */
  22. public class MasterThread {
  23. private static int SLAVE_ENGINE_NUMBER_MAX = 100;
  24. private static int _BLOCKSIZE = 5000;
  25. private static MasterThread masterThread;
  26. private BlockingQueue<MetaData> metaDataQueue;
  27. private SlaveEngineThread slaveEngineThread;
  28. public static synchronized MasterThread getInstance() {
  29. if (masterThread == null) {
  30. masterThread = new MasterThread();
  31. }
  32. return masterThread;
  33. }
  34. private MasterThread() {
  35. metaDataQueue = new LinkedBlockingQueue<MetaData>(_BLOCKSIZE);
  36. startSlaveThreadEngine();
  37. }
  38. private void startSlaveThreadEngine() {
  39. slaveEngineThread = new SlaveEngineThread(SLAVE_ENGINE_NUMBER_MAX);
  40. slaveEngineThread.start();
  41. }
  42. public synchronized void put(MetaData object) {
  43. if (object == null)
  44. return;
  45. if (!metaDataQueue.offer(object)) {
  46. System.err.println("BlockingQueue is up to max size:"
  47. + metaDataQueue.size());
  48. }
  49. }
  50. private class SlaveEngineThread extends Thread {
  51. private ExecutorService executorService;
  52. private Map<Integer, SlaveThread> slaveThreadMap;
  53. //本示例采用一致性Hash算法,选择SlaveThread
  54. private KetamaNodeLocator<Integer> nodeLocator;
  55. public SlaveEngineThread(final int nThreads) {
  56. slaveThreadMap = new HashMap<Integer, SlaveThread>();
  57. // 创建线程池,并发执行SlaveThread
  58. executorService = Executors.newFixedThreadPool(nThreads);
  59. for (int i = 0; i < nThreads; i++) {
  60. SlaveThread command = new SlaveThread(i);
  61. executorService.execute(command);
  62. slaveThreadMap.put(i, command);
  63. }
  64. nodeLocator = new KetamaNodeLocator<Integer>(
  65. slaveThreadMap.keySet());
  66. }
  67. @Override
  68. public void run() {
  69. while (true) {
  70. MetaData metaData = null;
  71. try {
  72. // 堵塞获取
  73. metaData = metaDataQueue.take();
  74. // 通过hash算法获取slaveThread编号
  75. Integer nodeNumber = nodeLocator.getNode(metaData.getId());
  76. SlaveThread slaveThread = slaveThreadMap.get(nodeNumber);
  77. if (slaveThread != null) {
  78. // 将MetaData存入SlaveThread处理
  79. slaveThread.put(metaData);
  80. }
  81. } catch (InterruptedException e) {
  82. executorService.shutdown();
  83. System.err.println("SlaveEngineThread is Interrupted ... ");
  84. break;
  85. } catch (Exception e) {
  86. System.err.println("failed to handle meta data" + e);
  87. }
  88. }
  89. System.err.println("Master Thread exit...");
  90. }
  91. }
  92. public void exit() {
  93. stopSlaveEngineThread();
  94. }
  95. private void stopSlaveEngineThread() {
  96. slaveEngineThread.interrupt();
  97. }
  98. }

SlaveThread.java
  1. package ll.concurrent.BlockingQueue;
  2. import java.text.SimpleDateFormat;
  3. import java.util.concurrent.BlockingQueue;
  4. import java.util.concurrent.LinkedBlockingQueue;
  5. public class SlaveThread implements Runnable {
  6. private BlockingQueue<MetaData> msgQueue = new LinkedBlockingQueue<MetaData>(
  7. 200);
  8. private int slaveThreadID;
  9. public SlaveThread() {
  10. }
  11. public SlaveThread(int slaveThreadID) {
  12. super();
  13. this.slaveThreadID = slaveThreadID;
  14. }
  15. public void run() {
  16. while (true) {
  17. MetaData metaData = null;
  18. try {
  19. // 堵塞获取
  20. metaData = msgQueue.take();
  21. handleMetaData(metaData);
  22. } catch (InterruptedException e) {
  23. System.err.println("SlaveThread[" + this.slaveThreadID + "] is Interrupted...");
  24. break;
  25. } catch (Exception e) {
  26. System.err.println("failed to handle meta data" + e);
  27. }
  28. }
  29. System.err.println("SlaveThread[" + this.slaveThreadID + "] exit...");
  30. }
  31. public void put(MetaData object) {
  32. if (object == null)
  33. return;
  34. if (!msgQueue.offer(object)) {
  35. System.err.println("SlaveThread BlockingQueue up to max size");
  36. }
  37. }
  38. private void handleMetaData(MetaData metaData) throws Exception {
  39. // 模拟处理,耗时500毫秒
  40. Thread.sleep(500);
  41. System.out.println("SlaveThread["+ this.slaveThreadID + "] "
  42. + metaData.toString()
  43. + new SimpleDateFormat("HH:mm:ss").format(System
  44. .currentTimeMillis()));
  45. }
  46. }

MetaData.java
  1. package ll.concurrent.BlockingQueue;
  2. public class MetaData {
  3. private String id;
  4. private String desc;
  5. public MetaData() {
  6. super();
  7. }
  8. public MetaData(String id) {
  9. super();
  10. this.id = id;
  11. }
  12. public MetaData(String id, String desc) {
  13. super();
  14. this.id = id;
  15. this.desc = desc;
  16. }
  17. public String getId() {
  18. return id;
  19. }
  20. public void setId(String id) {
  21. this.id = id;
  22. }
  23. public String getDesc() {
  24. return desc;
  25. }
  26. public void setDesc(String desc) {
  27. this.desc = desc;
  28. }
  29. @Override
  30. public String toString() {
  31. return "MetaData [id=" + id + ", desc=" + desc + "]";
  32. }
  33. }

TestCase.java
  1. package ll.concurrent.BlockingQueue;
  2. import java.text.SimpleDateFormat;
  3. public class TestCase {
  4. public static void main(String[] args) throws InterruptedException {
  5. MasterThread masterThread = MasterThread.getInstance();
  6. System.out.println("Start time:"
  7. + new SimpleDateFormat("HH:mm:ss").format(System
  8. .currentTimeMillis()));
  9. /**
  10. * 每个MetaData都需要0.5S的处理时间,如果串行执行,则需要500*0.5=250;
  11. * 现采用并行处理,只需要很短的时间即可执行完;
  12. */
  13. for (int i = 0; i < 500; i++) {
  14. MetaData metaData = new MetaData(i + "");
  15. masterThread.put(metaData);
  16. }
  17. }
  18. }



参考:
《Java编发编程实战》:7.2.1 日志服务章节: <N生产者,1个消费者> 

【并发编程】使用BlockingQueue实现<多生产者,多消费者>的更多相关文章

  1. Java并发编程虚假唤醒问题(生产者和消费者关系)

    何为虚假唤醒: 当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功:比如买货:如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买, ...

  2. 转: 【Java并发编程】之十三:生产者—消费者模型(含代码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一 ...

  3. 【Java并发编程】之十三:生产者—消费者模型

    生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. ​ 这里实现如下情况的生产--消费模型: ​ 生产者不断交替地生产两组 ...

  4. python并发编程-进程间通信-Queue队列使用-生产者消费者模型-线程理论-创建及对象属性方法-线程互斥锁-守护线程-02

    目录 进程补充 进程通信前言 Queue队列的基本使用 通过Queue队列实现进程间通信(IPC机制) 生产者消费者模型 以做包子买包子为例实现当包子卖完了停止消费行为 线程 什么是线程 为什么要有线 ...

  5. 并发编程 06—— CompletionService :Executor 和 BlockingQueue

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  6. 并发编程 01—— ThreadLocal

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  7. 并发编程 20—— AbstractQueuedSynchronizer 深入分析

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  8. 并发编程 02—— ConcurrentHashMap

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  9. 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  10. 并发编程 05—— Callable和Future

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

随机推荐

  1. hdoj 2159 (带限制的完全背包)

    #include <iostream> #include <algorithm> #include <cstring> using namespace std; ] ...

  2. BFS广度优先搜索 poj1915

    Knight Moves Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 25909 Accepted: 12244 Descri ...

  3. RESTful规范(一)

    一.学习restframework之前准备 1.json格式若想展示中文,需要ensure_ascii=False import json dic={'name':'你好'} print(json.d ...

  4. linux下文件校验的使用

    为解决官方发布的软件包被别人更改或者软件在传输过程中出现传输错误等问题,软件官方在提供软件包的同时,还提供一个保存MD5校验码的文件. Linux/unix中可以使用如下命令获得校验码和官方的校验码对 ...

  5. CF使用TGP下载后,分卷文件损坏的解决方法

    首先从游戏的列表删除游戏(安装失败出现分卷文件损坏的游戏) 然后进入游戏重新,继续找到该游戏(安装失败的游戏) 点击下载游戏!不会重新下载的,之后下载一些失败的文件,不会花费多少时间,慢慢等待即可 之 ...

  6. 【NOI2005】 聪聪可可

    树分治劲啊 原题: 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了, ...

  7. LG1861 星之器

    题意 题目背景 Magic Land 上的时间又过了若干世纪„„ 现在, 人们谈论着一个传说:从前,他们的祖先来到了一个位于东方的岛屿, 那里简直就是另外一个世界.善于分析与构造的 Magic Lan ...

  8. react-redux 的使用

    1 安装react-redux: npm install --save react-redux 2.之前使用redux的store.subscribe监听 store的状态改变,通过store.get ...

  9. day 60 Bootstrip学习

    图标地址 http://fontawesome.io/icons/ 图标用法地址 http://fontawesome.io/examples/ 实现代码 <!DOCTYPE html> ...

  10. 写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和

    例如,调用DigitSum(1729),则应该返回1+7+2+9,它的和是19. 思路:我们可以先将整数的每一个数字取出来,每次取个位数字,取完后退位(将数字除以10),在取个位数字,依次取出所有的数 ...