从多个生产者密钥中公平出列的队列

时间:2016-09-17 09:40:27

标签: java concurrency deque concurrenthashmap lifo

我有这种情况,我收到来自数千个来源的事件。每个来源都在发送有关其当前状态的信息。虽然我确实希望处理所有事件,但更重要的是首先处理每个源的最新事件,以便当前视图是最新的。所以我考虑使用ConcurrentHashMap将每个源的标识符作为键,并使用LIFO队列(堆栈)作为值。然后我会遍历RequestContext.getCurrentInstance().update(update); 的键,然后从每个源的堆栈中弹出一个项目。

我担心的是,当我迭代密钥并从每个密钥的队列中取出项目时,生产者可以在队列上发布新事件,可能会产生并发问题。制作人还可以向地图添加新密钥,并且遍历Map的{​​{3}}似乎是微弱的一致。这不是一个大问题,因为新项目将在后续迭代中处理。理想情况下,我还可以在Map的流上使用一些并行处理来加速该过程。

我想知道是否有更清洁的方法。实际上我可以使用LIFO entrySet并首先处理最新事件,但这种方法的问题在于存在一个源可以发送比其他事件更多事件的风险,因此可能比其他事件处理更多事件

我可以调查其他任何提供此类行为的数据结构吗?基本上我正在寻找的是一种优先考虑来自每个来源的事件的方法,同时给予消费者处理每个来源的公平机会。

2 个答案:

答案 0 :(得分:0)

您是否考虑过LIFO队列的FIFO队列?每个源都添加到其LIFO队列,并且为了处理,您从FIFO队列中获取第一个LIFO队列,处理一个事件,然后将其放回FIFO队列。这样你也可以对新的源没有任何问题,因为它们的LIFO队列将被简单地添加到FIFO队列中。

为了将事件添加到正确的LIFO队列,您可以维护一个知道每个源队列的额外HashMap,如果发生了一个尚未在Map中的新源,您知道必须将其LIFO队列添加到FIFO队列。

答案 1 :(得分:0)

我建议您构建自己的结构来管理它,因为它会为您的用例增加灵活性(和速度)。

我会使用循环队列来存储每个LIFO队列(堆栈)。循环队列是向尾部添加元素,从头部读取(但不删除)的队列。一旦头=尾,你重新开始。

您可以使用简单数组构建自己的队列。围绕操作管理同步并不困难,例如向阵列添加更多队列 - 并在需要时扩展它。我相信在数组中添加队列并不是你经常做的事情。

这很容易管理,您可以扩展循环队列来计算条目的访问频率,并限制访问其条目的频率(通过添加/删除使用者线程,甚至让它们等待一会儿从一个条目管理的堆栈消费。)

当使用多个线程从循环队列中读取元素时,甚至可以避免线程锁定,方法是在从堆栈中使用之前调用它们进行“注册”操作:每个线程都有自己的ID,当它“注册”时,存储ID在给定的队列条目。在注册之前和从堆栈弹出之前,线程执行“读取注册ID”操作,并且返回的ID必须与其自己的ID匹配。这意味着只有“拥有”给定队列条目的线程才能从该堆栈中弹出。如果注册过程的注册/确认失败,则意味着另一个线程正在使用该条目,因此当前线程将移动到下一个可用条目。

我过去曾经使用过这种策略,它的魅力就像魅力一样。我希望这对你有意义。