【问题标题】:Blocking FIFO Queue with Ability to Skip Elements?阻止具有跳过元素的能力的 FIFO 队列?
【发布时间】:2018-05-31 15:26:39
【问题描述】:

短版:我怎样才能最好地在 Java 中实现阻塞 FIFO 队列,如果队列中的项目当时不满足某些条件,则能够暂时跳过或跳过它们从队列中弹出?

加长版:

多年来,我一直在一个应用程序中使用 ArrayBlockingQueue,它对我的​​目的运行良好。到目前为止,我只需要调用 put() 和 take()。效果很好。

现在需要一个元素在通过 take() 检索时满足某些条件。如果它不符合标准,它应该回到队列中,但在之前的相同位置。

想象一下国际机场的海关排队。出于某种原因,乘客在上线时只收到了报关单。乘客们都在疯狂地涂鸦,以在轮到他们之前完成他们的表格。队伍前面有一名保安。当海关官员准备好迎接下一位乘客时,保安检查线路上的第一位乘客是否填写了海关申报单。如果是这样,他会将乘客送到海关官员那里。如果不是,他检查第二个乘客,然后检查第三个,等等,直到他找到完成的人。他把那个人送到海关官员那里。每次海关官员有空时都会发生同样的情况,总是从线路上的第一位乘客出发。

在研究中,我唯一想到的是使用双端队列(deque)并将元素从前面取出,直到找到符合条件的元素。然后按照我取下它们的相反顺序将元素放回前面。

有人推荐吗?

【问题讨论】:

    标签: java queue


    【解决方案1】:

    2 条可能的建议取决于您是否能够监听项目的状态变化:

    • 如果物品可以在准备就绪时通知您,那么只需在它们到达时给它们编号,并在它们准备好后立即将它们移动到PriorityQueue。然后只需从 PriorityQueue 中拉出第一项,如果它为空则阻塞。

    • 如果您必须检查每个项目以确定其状态是否已更改,那么您别无选择,只能依次访问每个项目,从最旧的开始,直到找到一个准备好了。在这种情况下,您真的不需要 Queue 作为底层数据结构; LinkedList 实际上更适合。

    第二种情况不仅速度较慢,而且处理未就绪的完整队列也更糟糕;要么您在重新启动之前在列表末尾暂停一段时间(同时阻塞),要么您的阻塞行为等同于忙等待,因为它反复循环遍历项目。

    (如果我在执行第二个时遇到困难,我倾向于尝试根据等待准备就绪的累积时间总量和至少一个的预期概率动态调整重启前的等待时间下次我开始遍历列表时完成。)

    【讨论】:

      【解决方案2】:

      让您的结构创建第二个队列。弹出时获取整个结构的写锁。暂时忽略主队列,先检查次队列。如果为空,则转到主队列。从主队列中弹出一个元素。如果它准备好了,拿走它并释放锁。如果不是,则将其放入辅助队列,然后再抓另一个。重复直到你准备好。

      如果您第一次尝试抓取辅助队列时它不是空的,请循环遍历辅助队列以查看它们是否已准备好。

      这样做的好处是你总能找到现在准备好的人。当然,除非你用尽了你的主队列,但是没有人准备好了,所以你无能为力。

      这样做的缺点是,如果你有一些超级慢的人,辅助队列可能是个问题。您可以通过提供剩余时间或其他东西的估计来解决这个问题。此外,坏演员总是有可能撒谎或以其他方式束缚你。但是,如果您无法抢占不良行为者,那么无论如何您都会遇到多线程问题。

      这是该算法的非线程安全版本 - 它只是在我的脑海中浮现,所以对它持保留态度。

      class SnappyQueue<E> {
          Queue<E> main = ... // people waiting in line
          Queue<E> slugs = ... // people at the front but still writing
          void push(E e) { main.addLast(e); }
          E pop() {
              E first = slugs.peek();
              if(first != null) {
                  for(E cur = slugs.pop(); cur != first; cur = slugs.pop()) {
                      if(cur.isReady()) return cur; // we're done, one of the slugs is ready
                      slugs.push(cur); // this slug isn't ready, put it back
                  }
              }
              while(true) {
                  E cur = main.pop();
                  if(cur == null) return null; // nothing left
                  if(cur.isReady()) return cur; // we found someone ready
                  slugs.push(cur); // not ready, push them into the slug line
              }
          }
      }
      

      【讨论】:

      • 通过将项目弹出并推送到第二个队列(您确定您在谈论队列吗?),您会弄乱顺序。假设您已将 3 个项目移动到辅助队列 [1、2、3]。在poll() 上,您检查 1;如果它还没有准备好,你offer()它。现在你的队列是 [2, 3, 1]。接下来poll() 返回 2,它已经准备好了,所以你返回它。下一次迭代,你有队列 [3, 1]。如果他们都准备好了,你会退回错误的。
      • @DavidW 我不会弄乱订单。项目本身在到达队列末尾时还没有准备好,从而弄乱了订单。到那时所有的赌注都结束了。
      猜你喜欢
      • 1970-01-01
      • 2015-01-26
      • 1970-01-01
      • 2015-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      相关资源
      最近更新 更多