3

对不起,令人困惑的标题。不知道如何表达它,这可能是问题!

我正在寻找一个很好的抽象来用于涉及并发线程的情况。

我已经能够接近,但不是很清楚。

稍微简化一下,我在 Android 手机上收集了两种传感器输入 - 方向类型的东西和 WiFi 扫描。

当收集到足够的两者时,我想触发一个使用数据的事件。但是,我不想停在那里——这个过程应该继续 N 次。

起初我只是在条件上使用了while循环,例如

startCollecting();
while (samplesCollected < X){
    // wait
    while (directionCount < Y || scanCount < Z){}; 
    // then
    doSomeStuff();
    samplesCollected++; 
}
stopCollecting();

但是,SO 告诉我这是一个糟糕的表现,而且我确实遇到了一些 UI 锁定(即使它在不同的线程上),所以我决定使用 java.util.concurrent。

问题是我不能完全确定使用哪个抽象,可能是因为我没有经验。

  • ReentrantLock 的条件:

    条件的想法似乎很棒 - 但我不想控制共享资源 - 我希望数据收集在处理第一批时在后台继续 - 那么我在哪里调用锁定?如果我不锁定,则会引发 IllegalMonitorStateException。

  • 倒计时锁:

    看起来很理想 - 当收集线程有可用数据时,它们可以调用 countDown(),并且当 countDown 被调用足够多次时,操作可以继续。但是 countDown 应该是一次性执行,我需要重复几次。

  • 循环障碍:

    CountdownLatch 的文档建议如果您希望行为可重复,则应使用 CyclicBarrier - 但 CyclicBarrier 的隐喻在这种情况下似乎完全错误,我不明白如何使用它。

我在下面链接了一些相关问题 - 任何指导将不胜感激。

效率 - 在等待变量更改的循环中使用 Thread.yield

如何让 Java 线程等待另一个线程的输出?

java中是否有“阻塞直到条件变为真”功能?

java中使用wait()和notify()的简单场景

4

3 回答 3

2

您可能希望使用并发队列(BlockingQueue各种)。

您从读取传感器的线程中填充队列(而不是将它们放入您现在放入的任何结构中)。

在您的循环中,take一条数据,检查它是什么(方向或wifi),增加正确的计数器,并将数据放在某个地方(可能是某种本地列表)。一旦你有足够的数据,将你收集的数据传递给你的处理函数。

这是因为您的线程在尝试从队列中获取某些内容并且没有可用的内容时处于休眠状态,因此它不会坐在那里轮询计数器。

于 2013-05-02T03:18:41.990 回答
2

我们的代码中有类似的实现。我们创建了一个内部类,它实现了可运行并可以处理数据。我们继续在单个线程中读取数据,一旦数据大小达到特定限制,我们就会将该数据传递给内部类的实例,并将该内部类实例作为任务提交给 ThreadPoolExecutor 服务。

这对我们非常有效。

于 2013-05-02T03:25:52.207 回答
1

您的应用程序被锁定,因为那段代码正忙于等待,这通常是一件坏事。为了快速修复,您可以Thread.sleep(25)在内部 while 循环中添加一个(或等效的),这应该可以修复锁定。

结合其他的东西...首先关闭变量samplesCollected, directionCount, 和scanCount应该被标记volatile或者是AtomicLong (or AtomicInteger)。否则,您不能保证在另一个线程中看到对它们所做的更改。阅读内存屏障,更具体地了解 Java 内存模型以了解原因。

如果您确保变量是线程安全的并添加Thread.sleep(...)您应该没问题(它不会锁定您的应用程序并且应该正常运行),尽管它不是一个理想的解决方案。

虽然这不是一个理想的解决方案。您可以通过让工作线程自己在每次增量后检查它是否高于阈值来摆脱这个主线程。如果是这样,那么他们可以启动一个线程(或在同一个线程中)执行您的聚合后代码。同样,如果它达到某个最大阈值,您可以向所有线程发出信号以停止收集。您需要使用AtomicLong.incrementAndGet()它才能正常工作,但要确保线程正确处理计数(volatile不起作用)。

于 2013-05-02T03:29:43.610 回答