对于令人困惑的标题感到抱歉。不确定如何表达它,这可能是问题!
我正在寻找一个好的抽象来用于涉及并发线程的情况。
我已经能够接近,但并没有完全钉住它。
稍微简化,我在Android手机上收集了两种传感器输入 - 方向类型的东西和WiFi扫描。
当收集到足够的两者时,我想触发使用数据的事件。但是,我不想停在那里 - 这个过程应该继续N次。
起初我只是在条件上使用了while循环,例如
startCollecting();
while (samplesCollected < X){
// wait
while (directionCount < Y || scanCount < Z){};
// then
doSomeStuff();
samplesCollected++;
}
stopCollecting();
然而,我被SO告知这是一个表现不佳的人,事实上我已经遇到了一些UI的锁定(即使它在不同的线程上),所以我决定使用java.util。并发的。
问题在于我无法确定使用哪种抽象,可能是由于我的经验不足。
ReentrantLock上的条件:
条件的想法看起来很棒 - 但事实并非我想要控制共享资源 - 我希望数据收集在后台继续进行,同时处理第一批 - 所以我在哪里调用锁?如果我没有锁定,那么它会抛出IllegalMonitorStateException。
CountdownLatch:
似乎很理想 - 当收集线程有数据可用时,它们可以调用countDown(),并且当countDown被调用足够多次时,该操作可以继续。但是countDown应该是一次性执行,我需要重复几次。
的CyclicBarrier:
如果你希望行为是可重复的,那么CountdownLatch的文档建议应该使用CyclicBarrier - 但是对于这种情况,CyclicBarrier的比喻似乎完全错误,我不明白如何使用它。我已将以下几个相关问题联系起来了 - 我们非常感谢您提供任何指导。
Efficiency - use of Thread.yield in loop that waits for variable change
How to make a Java thread wait for another thread's output?
is there a 'block until condition becomes true' function in java?
答案 0 :(得分:2)
您可能希望使用并发队列(BlockingQueue
种类)。
您从读取传感器的线程中填充队列(而不是将它们放入您现在放置的任何结构中)。
在你的循环中,take
一段数据,检查它是什么(方向或wifi),增加正确的计数器,并将数据放在某处(可能是某种类型的本地列表)。获得足够的数据后,将收集的数据传递给处理函数。
这是有效的,因为你的线程在尝试从队列中获取某些内容并且没有任何内容可以休眠时,因此它不会围绕轮询计数器。
答案 1 :(得分:2)
我们的代码中有类似的实现。 我们创建了一个实现runnable的内部类,可以处理数据。 我们继续在单个线程中读取数据,一旦数据大小达到特定限制,我们将该数据传递给内部类的实例,并将该内部类实例作为任务提交给ThreadPoolExecutor服务。
这对我们非常有用。
答案 2 :(得分:1)
你的应用被锁定了,因为这段代码是busy waiting,这通常是一件坏事。为了快速修复,您可以在内部while循环中添加Thread.sleep(25)
(或等效),这应该可以解决锁定问题。
结合其他事情......首先,变量samplesCollected
,directionCount
和scanCount
应标记为volatile
或AtomicLong (or AtomicInteger
)。否则,您无法保证在另一个线程中看到对它们所做的更改。阅读memory barriers,更具体地说,了解Java内存模型以了解原因。
如果你确保变量是线程安全的并添加Thread.sleep(...)
你应该没问题(它不会锁定你的应用程序并且应该正常运行),尽管它不是一个理想的解决方案。
但这不是一个理想的解决方案。如果工作线程在每个增量之后检查它是否高于阈值,则可以摆脱这个主线程。如果是这样,那么他们可以启动一个线程(或在同一个线程中)执行您的聚合后代码。同样,如果达到某个最大阈值,您可以通知所有线程停止收集。您需要使用AtomicLong.incrementAndGet()
才能正常工作,以确保线程正确处理计数(volatile
将不工作)。