如何使用 ConcurrentLinkedQueue?编辑:最后:

2022-08-31 10:00:16

如何在 Java 中使用 ?
使用这个,我需要担心队列中的并发性吗?或者我只需要定义两种方法(一个用于从列表中检索元素,另一个用于向列表中添加元素)?
注意:显然这两种方法必须同步。右?ConcurrentLinkedQueueLinkedQueue


编辑:我试图做的是这样的:我有一个类(在Java中),其中一个方法从队列中检索项目,另一个类使用一个方法将项目添加到队列中。从列表中添加和检索的项是我自己类的对象。

还有一个问题:我需要在 remove 方法中执行此操作吗:

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

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


答案 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();

有关更多信息,请参阅 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。如果是这样,请等待(),然后重试。无需锁定。

最后:

老实说,我只使用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 亿个对象放入队列中。


答案 2

这在很大程度上是另一个问题的重复

以下是与此问题相关的答案部分:

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

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

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

queue.add(obj);

queue.poll(obj);

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

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

最后一个线程不是线程安全的,因为在调用时间 isEmpty 和调用轮询时间之间,其他线程很可能在队列中添加或删除了项目。执行此操作的线程安全方法如下所示:

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

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


推荐