【问题标题】:How to use ConcurrentLinkedQueue?如何使用 ConcurrentLinkedQueue?
【发布时间】:2010-10-11 14:47:56
【问题描述】:

如何在 Java 中使用 ConcurrentLinkedQueue
使用这个LinkedQueue,我需要担心队列中的并发吗?还是我只需要定义两种方法(一种用于从列表中检索元素,另一种用于将元素添加到列表中)?
注意:显然这两种方法必须同步。对吧?


编辑: 我想要做的是:我有一个类(在 Java 中),它具有一种从队列中检索项目的方法,而另一个类具有一种将项目添加到队列。从列表中添加和检索的项目是我自己的类的对象。

还有一个问题:我需要在remove方法中这样做吗:

while (queue.size() == 0){ 
  wait(); 
  queue.poll();
}

我只有一个消费者和一个生产者。

【问题讨论】:

  • 感谢您对 mt 问题的回复。我想要做的是:我有一个类(在Java中),它具有一种从队列中检索项目的方法,而另一个类具有一种将项目添加到队列中的方法。从列表中添加和检索的项目是我自己的类的对象。
  • 您应该编辑您的问题并将此说明放在问题本身中。

标签: java concurrency


【解决方案1】:

不,方法不需要同步,也不需要定义任何方法;它们已经在 ConcurrentLinkedQueue 中,只需使用它们。 ConcurrentLinkedQueue 在内部完成您需要的所有锁定和其他操作;您的生产者将数据添加到队列中,然后您的消费者轮询它。

首先,创建您的队列:

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

现在,无论您在哪里创建生产者/消费者对象,都可以传入队列,以便他们有地方放置对象(您可以为此使用 setter,但我更喜欢在构造函数中执行此类操作):

YourProducer producer = new YourProducer(queue);

和:

YourConsumer consumer = new YourConsumer(queue);

并在您的生产者中添加内容:

queue.offer(myObject);

然后从你的消费者中取出东西(如果队列是空的,poll() 将返回 null,所以检查一下):

YourObject myObject = queue.poll();

更多信息见the Javadoc

编辑:

如果您需要阻止等待队列不为空,您可能需要使用LinkedBlockingQueue,并使用 take() 方法。但是,LinkedBlockingQueue 具有最大容量(默认为 Integer.MAX_VALUE,超过 20 亿),因此根据您的情况可能合适也可能不合适。

如果您只有一个线程将内容放入队列,而另一个线程将内容从队列中取出,则 ConcurrentLinkedQueue 可能是矫枉过正。当您可能有数百甚至数千个线程同时访问队列时,它更有用。您的需求可能会通过以下方式得到满足:

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

这样做的一个优点是它锁定实例(队列),因此您可以在队列上同步以确保复合操作的原子性(正如 Jared 所解释的)。您不能使用 ConcurrentLinkedQueue 执行此操作,因为所有操作都是在没有锁定实例的情况下完成的(使用 java.util.concurrent.atomic 变量)。如果您想在队列为空时阻塞,则不需要这样做,因为 poll() 将在队列为空时简单地返回 null,而 poll() 是原子的。检查 poll() 是否返回 null。如果是,wait(),然后再试一次。无需锁定。

最后:

老实说,我只是使用 LinkedBlockingQueue。对于您的应用程序来说,它仍然是矫枉过正,但它很可能会正常工作。如果性能不够(PROFILE!),您可以随时尝试其他方法,这意味着您不必处理任何同步的东西:

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

其他一切都是一样的。 Put 可能不会阻塞,因为您不太可能将 20 亿个对象放入队列中。

【讨论】:

  • 感谢您的回复。还有一个问题:我是否需要在 remove 方法中这样做:while (queue.size() == 0) wait(); queue.poll();
  • 我将回答这个问题作为对我答案的修改,因为它非常重要。
  • 由于在另一个问题中造成混淆,Collection.synchronizedList 返回一个 List,它没有实现 Queue
  • @AdamJaskiewicz 使用ConcurrentLinkedQueue 表示生产者消费者是个好主意,我指的是这个帖子stackoverflow.com/questions/1426754/…
【解决方案2】:

这主要是duplicate of another question

这是与该问题相关的答案部分:

如果我使用 java.util.ConcurrentLinkedQueue,是否需要自己进行同步?

并发集合上的原子操作会为您同步。换句话说,对队列的每个单独调用都可以保证线程安全,而无需您进行任何操作。 保证线程安全的是您对集合执行的任何非原子操作。

例如,这是线程安全的,无需您进行任何操作:

queue.add(obj);

queue.poll(obj);

但是;对队列的非原子调用不是自动线程安全的。例如,以下操作不是自动线程安全的:

if(!queue.isEmpty()) {
   queue.poll(obj);
}

最后一个不是线程安全的,因为很有可能在调用 isEmpty 和调用 poll 之间,其他线程会从队列中添加或删除项目。执行此操作的线程安全方式如下:

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

再次...对队列的原子调用是自动线程安全的。非原子调用不是。

【讨论】:

  • 我唯一能做的就是阻塞直到队列非空。使用 BlockingQueue 的实现不是更好的服务吗(take() 是原子的并且阻塞直到有东西要消耗)?
  • 你也需要小心。与 synchronizedList 不同,ConcurrentLinkedQueue 不会自行同步,因此在您的代码中,当您处于同步块中时,生产者仍然可以向队列提供服务。
  • 为什么要结合 queue.isEmpty() 和 queue.poll()?您不能只轮询并检查结果是否为空吗? (我的理解是,如果你轮询一个空队列,它会返回 null)
  • 我同意您代码中的所有内容,除了最后一个代码块。在 ConcurrentLInkedQueue 上同步并不能保证任何事情,因为其他调用未同步。因此,即使您已同步该线程,您的“isEmpty()”和“poll(...)”之间的另一个线程也可能会发生“add(..)”
【解决方案3】:

这可能是您在尝试使用队列中的所有内容时在线程安全和“漂亮”方面所寻找的:

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

这将保证您在队列为空时退出,并且只要队列不为空,您就会继续从中弹出对象。

【讨论】:

  • 对于清空队列非常有用。谢谢。
【解决方案4】:

使用poll 获取第一个元素,使用add 添加新的最后一个元素。就是这样,没有同步或其他任何东西。

【讨论】:

    【解决方案5】:

    ConcurentLinkedQueue 是一种非常高效的无等待/无锁实现(请参阅 javadoc 以供参考),因此您不仅不需要同步,而且队列也不会锁定任何东西,因此几乎与非同步的一样快(不是线程安全的)一个。

    【讨论】:

      【解决方案6】:

      就像使用非并发集合一样使用它。 Concurrent[Collection] 类包装了常规集合,因此您不必考虑同步访问。

      编辑: ConcurrentLinkedList 实际上不仅仅是一个包装器,而是一个更好的并发实现。无论哪种方式,您都不必担心同步问题。

      【讨论】:

      • ConcurrentLinkedQueue 不是。它是专门为多个生产者和消费者的并发访问而构建的。比 Collections.synchronized* 返回的简单包装器更漂亮一点
      • 是的,在 J2SE 5 中添加了很多简洁的并发功能。
      猜你喜欢
      • 1970-01-01
      • 2010-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      • 2013-09-12
      • 2016-11-16
      相关资源
      最近更新 更多