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
VSB
),则返回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建议的单队列,但最终可能会返回到两个队列。实现也很好。在我的例子中,我有其他避免阻塞的原因,但如果我的生产者和消费者的耦合程度稍低一点,这看起来就不错了。