Java 观察者阻塞队列

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

我正在使用observer模式和BlockingQueue添加一些实例。现在,在另一种方法中,我使用了队列,但take()似乎一直在等待,尽管我是这样做的:

/** {@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){…}