【并发编程】使用BlockingQueue实现<多生产者,多消费者>

- 持有一个BlockingQueue队列,用于并发接收存储MetaData对象;
- 使用Hash一致性算法ketama,来选择SlaveThread节点;
- 从BlockingQueue队列中,取出MetaData对象,分配给各SlaveThread节点;
- SlaveThread节点负责真正处理MetaData对象;
- 持有一个BlockingQueue队列,用于存储MetaData对象;
- 负责真正处理MetaData对象;
- 假设处理每个MetaData对象需要耗时0.5秒,需要处理500个MetaData对象;
- 若是串行处理,则需要0.5*500=250秒;
- 使用上图所示的master/slave算法,则可以5秒内完成;
MasterThread.java
SlaveThread.java
测试


完整程序
package ll.concurrent.BlockingQueue.Ketama;import java.io.UnsupportedEncodingException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public enum HashAlgorithm {KETAMA_HASH;private static final String UTF_8 = "UTF-8";private static final String MD5 = "MD5";public long hash(byte[] digest, int nTime) {long rv = ((long) (digest[3 + nTime * 4] & 0xFF) << 24)| ((long) (digest[2 + nTime * 4] & 0xFF) << 16)| ((long) (digest[1 + nTime * 4] & 0xFF) << 8)| (digest[0 + nTime * 4] & 0xFF);return rv & 0xffffffffL; /* Truncate to 32-bits */}public byte[] computeMd5(String key) {MessageDigest md5;try {md5 = MessageDigest.getInstance(MD5);} catch (NoSuchAlgorithmException e) {throw new RuntimeException("MD5 not supported", e);}md5.reset();byte[] keyBytes = null;try {keyBytes = key.getBytes(UTF_8);} catch (UnsupportedEncodingException e) {throw new RuntimeException("Unknown string :" + key, e);}md5.update(keyBytes);return md5.digest();}}
package ll.concurrent.BlockingQueue.Ketama;import java.util.Collection;import java.util.TreeMap;/*** 一致性Hash算法:是一种分布式算法,常用于负载均衡;** @param <T>*/public final class KetamaNodeLocator<T> {private TreeMap<Long, T> ketamaNodes;private HashAlgorithm hashAlg;private int numReps = 160;public KetamaNodeLocator(Collection<T> nodes) {this(nodes, HashAlgorithm.KETAMA_HASH);}public KetamaNodeLocator(Collection<T> nodes, HashAlgorithm alg) {this(nodes, HashAlgorithm.KETAMA_HASH, 160);}public KetamaNodeLocator(Collection<T> nodes, HashAlgorithm alg,int nodeCopies) {hashAlg = alg;ketamaNodes = new TreeMap<Long, T>();numReps = nodeCopies;for (T node : nodes) {for (int i = 0; i < numReps / 4; i++) {byte[] digest = hashAlg.computeMd5(node.toString() + i);for (int h = 0; h < 4; h++) {long m = hashAlg.hash(digest, h);ketamaNodes.put(m, node);}}}}public T getNode(final String k) {byte[] digest = hashAlg.computeMd5(k);T rv = getNodeForKey(hashAlg.hash(digest, 0));return rv;}T getNodeForKey(long hash) {if (ketamaNodes.isEmpty()) {return null;}if (!ketamaNodes.containsKey(hash)) {Object ceilValue = ((TreeMap<Long, T>) ketamaNodes).ceilingKey(hash);if (ceilValue != null) {try {hash = Long.valueOf(ceilValue.toString());} catch (NumberFormatException e) {e.printStackTrace();hash = 0;}}if (ceilValue == null || hash == 0) {hash = ketamaNodes.firstKey();}}return ketamaNodes.get(hash);}}
package ll.concurrent.BlockingQueue;import java.util.HashMap;import java.util.Map;import java.util.concurrent.BlockingQueue;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.LinkedBlockingQueue;import ll.concurrent.BlockingQueue.Ketama.KetamaNodeLocator;/*** <pre>* MasterThread:* 1. 持有一个BlockingQueue队列,用于并发接收存储MetaData对象;* 2. 使用Hash一致性算法ketama来选择SlaveThread节点;* 3. 从BlockingQueue队列中,取出MetaData对象,分配给SlaveThread节点;* 4. SlaveThread节点负责真正处理MetaData对象;** SlaveThread:* 1. 持有一个BlockingQueue队列,用于存储MetaData对象;* 2. 负责真正处理MetaData对象;* </pre>*/public class MasterThread {private static int SLAVE_ENGINE_NUMBER_MAX = 100;private static int _BLOCKSIZE = 5000;private static MasterThread masterThread;private BlockingQueue<MetaData> metaDataQueue;private SlaveEngineThread slaveEngineThread;public static synchronized MasterThread getInstance() {if (masterThread == null) {masterThread = new MasterThread();}return masterThread;}private MasterThread() {metaDataQueue = new LinkedBlockingQueue<MetaData>(_BLOCKSIZE);startSlaveThreadEngine();}private void startSlaveThreadEngine() {slaveEngineThread = new SlaveEngineThread(SLAVE_ENGINE_NUMBER_MAX);slaveEngineThread.start();}public synchronized void put(MetaData object) {if (object == null)return;if (!metaDataQueue.offer(object)) {System.err.println("BlockingQueue is up to max size:"+ metaDataQueue.size());}}private class SlaveEngineThread extends Thread {private ExecutorService executorService;private Map<Integer, SlaveThread> slaveThreadMap;//本示例采用一致性Hash算法,选择SlaveThreadprivate KetamaNodeLocator<Integer> nodeLocator;public SlaveEngineThread(final int nThreads) {slaveThreadMap = new HashMap<Integer, SlaveThread>();// 创建线程池,并发执行SlaveThreadexecutorService = Executors.newFixedThreadPool(nThreads);for (int i = 0; i < nThreads; i++) {SlaveThread command = new SlaveThread(i);executorService.execute(command);slaveThreadMap.put(i, command);}nodeLocator = new KetamaNodeLocator<Integer>(slaveThreadMap.keySet());}@Overridepublic void run() {while (true) {MetaData metaData = null;try {// 堵塞获取metaData = metaDataQueue.take();// 通过hash算法获取slaveThread编号Integer nodeNumber = nodeLocator.getNode(metaData.getId());SlaveThread slaveThread = slaveThreadMap.get(nodeNumber);if (slaveThread != null) {// 将MetaData存入SlaveThread处理slaveThread.put(metaData);}} catch (InterruptedException e) {executorService.shutdown();System.err.println("SlaveEngineThread is Interrupted ... ");break;} catch (Exception e) {System.err.println("failed to handle meta data" + e);}}System.err.println("Master Thread exit...");}}public void exit() {stopSlaveEngineThread();}private void stopSlaveEngineThread() {slaveEngineThread.interrupt();}}
package ll.concurrent.BlockingQueue;import java.text.SimpleDateFormat;import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;public class SlaveThread implements Runnable {private BlockingQueue<MetaData> msgQueue = new LinkedBlockingQueue<MetaData>(200);private int slaveThreadID;public SlaveThread() {}public SlaveThread(int slaveThreadID) {super();this.slaveThreadID = slaveThreadID;}public void run() {while (true) {MetaData metaData = null;try {// 堵塞获取metaData = msgQueue.take();handleMetaData(metaData);} catch (InterruptedException e) {System.err.println("SlaveThread[" + this.slaveThreadID + "] is Interrupted...");break;} catch (Exception e) {System.err.println("failed to handle meta data" + e);}}System.err.println("SlaveThread[" + this.slaveThreadID + "] exit...");}public void put(MetaData object) {if (object == null)return;if (!msgQueue.offer(object)) {System.err.println("SlaveThread BlockingQueue up to max size");}}private void handleMetaData(MetaData metaData) throws Exception {// 模拟处理,耗时500毫秒Thread.sleep(500);System.out.println("SlaveThread["+ this.slaveThreadID + "] "+ metaData.toString()+ new SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis()));}}
package ll.concurrent.BlockingQueue;public class MetaData {private String id;private String desc;public MetaData() {super();}public MetaData(String id) {super();this.id = id;}public MetaData(String id, String desc) {super();this.id = id;this.desc = desc;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getDesc() {return desc;}public void setDesc(String desc) {this.desc = desc;}@Overridepublic String toString() {return "MetaData [id=" + id + ", desc=" + desc + "]";}}
package ll.concurrent.BlockingQueue;import java.text.SimpleDateFormat;public class TestCase {public static void main(String[] args) throws InterruptedException {MasterThread masterThread = MasterThread.getInstance();System.out.println("Start time:"+ new SimpleDateFormat("HH:mm:ss").format(System.currentTimeMillis()));/*** 每个MetaData都需要0.5S的处理时间,如果串行执行,则需要500*0.5=250;* 现采用并行处理,只需要很短的时间即可执行完;*/for (int i = 0; i < 500; i++) {MetaData metaData = new MetaData(i + "");masterThread.put(metaData);}}}
【并发编程】使用BlockingQueue实现<多生产者,多消费者>的更多相关文章
- Java并发编程虚假唤醒问题(生产者和消费者关系)
何为虚假唤醒: 当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功:比如买货:如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买, ...
- 转: 【Java并发编程】之十三:生产者—消费者模型(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一 ...
- 【Java并发编程】之十三:生产者—消费者模型
生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. 这里实现如下情况的生产--消费模型: 生产者不断交替地生产两组 ...
- python并发编程-进程间通信-Queue队列使用-生产者消费者模型-线程理论-创建及对象属性方法-线程互斥锁-守护线程-02
目录 进程补充 进程通信前言 Queue队列的基本使用 通过Queue队列实现进程间通信(IPC机制) 生产者消费者模型 以做包子买包子为例实现当包子卖完了停止消费行为 线程 什么是线程 为什么要有线 ...
- 并发编程 06—— CompletionService :Executor 和 BlockingQueue
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- 并发编程 01—— ThreadLocal
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- 并发编程 20—— AbstractQueuedSynchronizer 深入分析
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- 并发编程 02—— ConcurrentHashMap
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- 并发编程 05—— Callable和Future
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
随机推荐
- annotation注释简单介绍
元数据的作用 如果要对于元数据的作用进行分类,目前还没有明确的定义,不过我们可以根据它所起的作用,大致可分为三类: l 编写文档:通过代码里标识的元数据生成文档. l ...
- hdu2461 Rectangles 线段树--扫描线
You are developing a software for painting rectangles on the screen. The software supports drawing s ...
- Python 3 运行 shell 命令
#python 3.5 , win10 引入包 #os.chdir('path') import osimport subprocess #https://docs.python.org/3.5/li ...
- Go Example--if语句
package main import "fmt" func main() { //if else 条件都不需要括号,{}是需要的 if 7 % 2 == 0 { fmt.Prin ...
- 【BZOJ3144】【HNOI2013】切糕
总算做了一道2011以后的省选题了……原题: 图片题面好评! P,Q,R≤40,0≤D≤R,给出的所有的不和谐值不超过1000. 文本样例好评! 恩这个是听妹主席讲过后会写的,首先把每个点拆成链,那么 ...
- 【NOI2005】 聪聪可可
树分治劲啊 原题: 聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃.两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了, ...
- SQLyog中创建数据表及相关查询方法
USE du; /*1.创建账务表 id name mony*/ CREATE TABLE zhangwu(id INT PRIMARY KEY AUTO_INCREMENT, sname VARCH ...
- 记一次服务器路由跟踪 (2019-01-23 TODO)
记一次服务器路由跟踪 有用户反馈网站 无法访问. 现象如下: ping 没有反馈,确认了可以 ping 通其它的网站. tracert 跟踪到服务器商的内部就没的反应了. 同样一家的服务器商,另外一台 ...
- 昨天办了一张地铁卡,我想到一个app
如果成都开通 扫 二维码 做地铁.地铁是距离收费的,如果我们有一个平台,搜集所有用户的 入站 和出站二维码,然后重新分配,那么每个用户就只用 出 2块钱每次的最低消费了.哈哈............. ...
- laravel 打印完整sql语句
laravel5 用DB自带的getQueryLog方法直接打印: DB::connection()->enableQueryLog(); // 开启QueryLog \App\User::fi ...