32

我有几个ArrayLists作为数据队列工作。每个队列都链接到一个单独的线程,该线程检查其中是否ArrayList有一些数据。

 while (array.size == 0) {
    // nothing
 }

 // do stuff with one element of the array
 // remove element from array
 // and call the loop again

我在嵌入式系统编程中做过类似的事情,但是在 Java 中使用它是否安全?担心的是通过非常快速地迭代该while循环来浪费过程功率。

它可以通过添加Thread.sleep(100)并每 100 毫秒检查一次来解决,但又一次 - 响应时间较慢。

问题是 - 我是否需要添加睡眠或者我不应该担心这一点?

关于更安全/更好的系统来检查数组中的新数据有什么建议吗?

4

7 回答 7

52

ArrayList 不是线程安全的集合,因此如果一个线程将数据添加到您的列表中,而另一个线程尝试从同一个列表中检索数据,您无法保证另一个线程会看到添加的元素。

并且像您描述的那样忙于等待会不必要地消耗cpu资源。

既然您似乎需要一个队列,为什么不使用一个,例如ArrayBlockingQueue. 它有一个take方法可以阻塞,而不消耗 CPU 周期,直到一个项目被添加到队列中。它是线程安全的。

于 2012-08-28T12:52:21.720 回答
11

除非您需要等待的时间非常短,从而使上下文切换过于昂贵,否则我不会使用旋转。它肯定会无缘无故地浪费 CPU 周期。

您应该使用wait/notify或其他一些信号机制来暂停线程并仅在必要时唤醒它。

对于更高级的结构,生产者-消费者模式有专门的数据结构,比如BlockingQueue(选择一个实现):

一个队列,它还支持在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用的操作。

于 2012-08-28T12:51:10.233 回答
1

java.lang.ArrayList根本不是线程安全的。出于排队目的,最好使用BlockingQueue. 如果队列为空,它会阻塞线程调用而不消耗 cpu。您可以根据需要使用ArrayBlockingQueueorLinkedBlockingQueue或其他队列实现。

即使您可以使用它来实现它,wait and notifyAll但始终建议使用BlockingQueue.

于 2012-08-28T13:10:06.077 回答
1

使用 java 5 中发布的阻塞队列之类的东西怎么样。我认为现在建议使用这种方法,而不是等待/通知,这可能会变得非常复杂。我已经使用它并且效果很好。

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html

于 2012-08-28T12:52:52.737 回答
1

除了使用 ArrayList,您还可以使用 Concurrent 集合,例如 ArrayBlockingQueue

ArrayBlockingQueue<YourObject> theQueue;
while(true) {
  YourObject o = theQueue.take();
 //process your object
}

在另一个地方,你填满你的队列,你只需做一个

theQueue.add(theElement);

等待对象的线程将“休眠”直到有一个元素。add 方法将唤醒消费线程。

您可以在此处阅读有关此类的更多信息:http: //docs.oracle.com/javase/6/docs/api/java/util/concurrent/ArrayBlockingQueue.html

于 2012-08-28T13:14:33.320 回答
0

如果没有睡眠,您的线程将尽可能快地循环并访问 ArrayList,可能大多数时候没有任何结果。

我建议实施侦听器/观察者模式。如果可以的话,让填充 ArrayList 的生产者通知适当的线程进行更改。因此,您将从轮询行为转变为推送行为。

不确定这在您的架构中是否可行,但需要对您的系统进行进一步解释。

于 2012-08-28T12:53:16.760 回答
0

什么是轮询,它有什么问题?

反复测试条件直到它变为真的过程称为轮询。

轮询通常是在循环的帮助下实现的,以检查特定条件是否为真。如果属实,则采取某些行动。这会浪费许多 CPU 周期并使实现效率低下。例如,在一个经典的排队问题中,一个线程正在生产数据,而另一个线程正在使用它。

Java多线程如何解决这个问题?

为了避免轮询,Java 使用了三种方法,wait()notify()notifyAll()

所有这些方法都属于Object最终类,因此所有类都有它们。它们只能在同步块中使用。

wait() - 它告诉调用线程放弃锁并进入睡眠状态,直到其他线程进入同一个监视器并调用notify().

notify() - 它唤醒一个调用wait()同一对象的单个线程。需要注意的是,调用notify()实际上并没有放弃对资源的锁定。

notifyAll() - 它唤醒所有调用wait()同一个对象的线程。

ArrayList不是线程安全的集合。使用ArrayBlockingQueue.

类 ArrayBlockingQueue

于 2016-10-10T06:31:58.100 回答