Java 观察者阻塞队列
我正在使用observer模式和BlockingQueue添加一些实例。现在,在另一种方法中,我使用了队列,但take()似乎一直在等待,尽管我是这样做的:Java 观察者阻塞队列,java,observer-pattern,blockingqueue,Java,Observer Pattern,Blockingqueue,我正在使用observer模式和BlockingQueue添加一些实例。现在,在另一种方法中,我使用了队列,但take()似乎一直在等待,尽管我是这样做的: /** {@inheritDoc} */ @Override public void diffListener(final EDiff paramDiff, final IStructuralItem paramNewNode, final IStructuralItem paramOldNode, final DiffDepth
/** {@inheritDoc} */
@Override
public void diffListener(final EDiff paramDiff, final IStructuralItem paramNewNode,
final IStructuralItem paramOldNode, final DiffDepth paramDepth) {
final Diff diff =
new Diff(paramDiff, paramNewNode.getNodeKey(), paramOldNode.getNodeKey(), paramDepth);
mDiffs.add(diff);
try {
mDiffQueue.put(diff);
} catch (final InterruptedException e) {
LOGWRAPPER.error(e.getMessage(), e);
}
mEntries++;
if (mEntries == AFTER_COUNT_DIFFS) {
try {
mRunner.run(new PopulateDatabase(mDiffDatabase, mDiffs));
} catch (final Exception e) {
LOGWRAPPER.error(e.getMessage(), e);
}
mEntries = 0;
mDiffs = new LinkedList<>();
}
}
/** {@inheritDoc} */
@Override
public void diffDone() {
try {
mRunner.run(new PopulateDatabase(mDiffDatabase, mDiffs));
} catch (final Exception e) {
LOGWRAPPER.error(e.getMessage(), e);
}
mDone = true;
}
while (!(mDiffQueue.isEmpty() && mDone) || mDiffQueue.take().getDiff() == EDiff.INSERTED) {}
但是我认为第一个表达式被检查了,而mDone不是true,那么可能mDone被设置为true(观察者总是多线程的?),但它已经在调用mDiffQueue.take()?:-/
编辑:我现在真的不明白。我最近将其更改为:
synchronized (mDiffQueue) {
while (!(mDiffQueue.isEmpty() && mDone)) {
if (mDiffQueue.take().getDiff() != EDiff.INSERTED) {
break;
}
}
}
如果我在调试器中等待一点时间,它就会工作,但它也应该“实时”工作,因为mDone被初始化为false,因此while条件应该为true,主体应该被执行
如果mDiffQueue为空且mDone为true,则应跳过while循环的主体(这意味着队列不再被填充)
编辑:似乎是:
synchronized (mDiffQueue) {
while (!(mDiffQueue.isEmpty() && mDone)) {
if (mDiffQueue.peek() != null) {
if (mDiffQueue.take().getDiff() != EDiff.INSERTED) {
break;
}
}
}
}
尽管我不明白为什么peek()是强制性的
编辑:
我所做的是在树上迭代,我想跳过所有插入的节点:
for (final AbsAxis axis = new DescendantAxis(paramRtx, true); axis.hasNext(); axis.next()) {
skipInserts();
final IStructuralItem node = paramRtx.getStructuralNode();
if (node.hasFirstChild()) {
depth++;
skipInserts();
...
基本上计算树中的最大深度或级别,而不考虑在树的另一个修订版中删除的节点(用于比较Sunburst可视化),但好的,这可能超出范围。只是为了说明,我正在对尚未插入的节点执行某些操作,即使只是调整最大深度
问候,
Johannes是一个“阻塞调用”。这意味着它将阻塞(永远等待),直到队列中出现某个内容,然后返回添加的内容。当然,如果队列中有东西,它会立即返回
您可以使用
peek()
返回take()
将返回的内容-也就是说,peek()
返回下一项而不将其从队列中删除,或者如果队列中没有任何内容,则返回null
。尝试在测试中使用peek()
(但也要检查null)。第一个建议:不要synchronized(mDiffQueue)
。如果LinkedBlockingQueue有一些synchronized
方法,就会出现死锁;这里不是这样,但这是一种你应该避免的做法。无论如何,我不明白你为什么要在那一点上同步
您必须在等待检查是否设置了mDone
时定期“唤醒”:
while (!(mDiffQueue.isEmpty() && mDone)) {
// poll returns null if nothing is added in the queue for 0.1 second.
Diff diff = mDiffQueue.poll(0.1, TimeUnit.SECONDS);
if (diff != null)
process(diff);
}
这与使用peek
大致相同,但是peek
基本上是等待纳秒。使用peek
称为“忙等待”(线程不停地运行while循环),使用pool
称为“半忙等待”(让线程每隔一段时间休眠)
我猜在您的情况下,如果
diff
不是EDiff.INSERTED
类型,那么process(diff)
将退出循环。我不确定这是否就是你想要实现的目标。这看起来很奇怪,因为您基本上只是暂停消费线程,直到您得到一个正确类型的元素,然后您什么也不做。您无法接收未来的传入元素,因为您已退出while循环。是的,这就是为什么我引入了布尔成员变量,当不再遇到差异时,该变量设置为true,因此BlockingQueue不再填充,那么它应该跳过while主体。请参阅我对原始帖子的编辑:-/。我看不出peek()有什么帮助,因为有时队列当前可能为空,但之后可能会被填满。mDone在哪里设置为true或false?在diffDone()中,但我认为您忘记向下滚动了?;-)我的diffListener方法是从另一个线程调用的,所以我不知道它是否是线程安全的,只是想同步调用。这样我就可以安全地移除它了?但是好的,我假设take()和put()是线程安全的。我需要一些东西来检查mDiffQueue是否不再被填充(对于最后一个节点)。否则take()将永远阻塞。是的,您可以删除它。LinkedBlockingQueue本身同步性很好。我不理解你这段的最后一部分。我想我真的不明白你想做什么。从你原来的帖子上看不清楚。谁在排队,谁在排队?然后用它们做什么呢?Mh,observer类停止发送,然后调用diffDone(),因此我必须检查BlockingQueue是否不再填充(使用mDone,最后设置为true)。它们被放入public void diffListener中的队列中(final EDiff paramDiff,final IStructuralItem paramNewNode,final IStructuralItem paramOldNode,final DiffDepth paramDepth){…}