Java 协调多个并发队列

Java 协调多个并发队列,java,concurrency,queue,Java,Concurrency,Queue,我目前有一个并发队列实现,它使用BlockingQueue作为数据存储。现在,我需要引入第二种具有更高优先级的对象,将我引向原始队列的饥饿/优先级队列。因此,我们正在处理由多个线程生成的类型A和类型B的对象。任何类型B的对象都应在处理类型A的对象之前进行处理,但必须保持FIFO顺序以外的顺序。因此,如果插入{1A,1B,2A,3A,2B},顺序应该是{1B,2B,1A,2A,3A} 我尝试了一个PriorityBlockingQueue将类型Bs推到前面,但我无法维持FIFO要求(相同类型的项目

我目前有一个并发队列实现,它使用
BlockingQueue
作为数据存储。现在,我需要引入第二种具有更高优先级的对象,将我引向原始队列的饥饿/优先级队列。因此,我们正在处理由多个线程生成的类型A和类型B的对象。任何类型B的对象都应在处理类型A的对象之前进行处理,但必须保持FIFO顺序以外的顺序。因此,如果插入{1A,1B,2A,3A,2B},顺序应该是{1B,2B,1A,2A,3A}

我尝试了一个
PriorityBlockingQueue
将类型Bs推到前面,但我无法维持FIFO要求(相同类型的项目之间没有自然顺序)

我的下一个想法是使用两个并发队列。在协调两个队列之间的访问时,我正在寻找常见的问题或注意事项。理想情况下,我想做如下事情:

   public void add(A a)
   {
       aQueue.add(a);
   }
   public void add(B b)
   {
       bQueue.add(b);
   }

   private void consume() 
   {
       if(!bQueue.isEmpty())
          process(bQueue.poll());
       else if(!aQueue.isEmpty())
          process(aQueue.poll());
   }
如果两个队列都是
ConcurrentLinkedQueue
(或在此处插入更合适的结构),我是否需要任何同步或锁定?注意,我有许多生产者,但只有一个消费者(单线程
ThreadPoolExecutor


编辑:如果在isEmpty()检查之后出现了B,则可以处理a并在下一个consume()调用中处理它。

您需要同步getQueueItem()方法

使用A和B实现QueueItem接口

   public void add(A a)
   {
       aQueue.add(a);
   }
   public void add(B b)
   {
       bQueue.add(b);
   }

   private void consume() 
   {
       process(getNextItem());
   }

    private QueueItem getNextItem()
    {
       synchronized(bQueue) {
          if(!bQueue.isEmpty()) return bQueue.poll();
          return aQueue.poll();
       }
    }

我不确定我是否正确地理解了您的情况,但我认为应该可以使用单个队列来解决这个问题

您说过您的对象(在队列中)应该按自然顺序和类型进行比较。如果没有自然顺序,只需使用序列生成器(即,
AtomicLong
)即可为对象提供唯一且始终递增的队列ID。从
AtomicLong
获取数据不需要花费时间,除非您处于纳秒级

因此,您的
比较器.compare
应如下所示:

   public void add(A a)
   {
       aQueue.add(a);
   }
   public void add(B b)
   {
       bQueue.add(b);
   }

   private void consume() 
   {
       if(!bQueue.isEmpty())
          process(bQueue.poll());
       else if(!aQueue.isEmpty())
          process(aQueue.poll());
   }
1) 检查对象类型。如果不同(
A
VS
B
),则返回1/-1。否则,请参见下文
2) 检查id。它保证是不同的


如果您不能更改对象(A和B),您仍然可以将它们包装到另一个包含该ID的对象中。

根据您的要求,您在这里的方法应该可以正常工作

我将对您的代码进行以下更改。这将阻塞getNextItem(),直到其中一个队列为您返回一个对象

private Object block = new Object();

public void add(A a)
{
    synchronized( block )
    {
        aQueue.add( a );
        block.notifyAll();
    }
}

public void add(B b)
{
    synchronized( block )
    {
        bQueue.add( b );
        block.notifyAll();
    }
}

private Object consume() 
{
    Object value = null
    synchroinzed( block )
    {
        while ( return == null )
        {
            value = bQueue.poll();
            if ( value == null ) value = aQueue.poll();
            if ( value == null ) block.wait();
        }
     }

   return value;
}

在a对象之前处理B对象有多重要?在您当前的代码中,如果在isEmpty()调用之后插入B对象,您将错过它。有两种不同的答案,取决于你必须做什么…@乔纳森不重要。我本想加上这一点,但不知怎的把它漏掉了。问题更新。我唯一关心的是包装和使用增量是这里推送的对象的绝对数量。这是一个权衡:)你得到了简单性,并损失了一些GC时间。这完全取决于队列的强度、内存限制等。进行测量并确定。。。我会选择简单,但我不知道完整的上下文。我将尝试一下。我绝对相信java并发开发人员胜过我所做的一切。现有的实现在生产过程中已经被反复使用了很多年,我不想对它进行大幅度的修改。我也喜欢在不可避免地添加新类型时我可以处理它们(在A之前使用C,但在B-ack之后使用C!),谢谢你的回答。我将尝试@mindas建议的单队列,但最终可能会返回到两个队列。实现也很好。在我的例子中,我有其他避免阻塞的原因,但如果我的生产者和消费者的耦合程度稍低一点,这看起来就不错了。