我应该在Java中使用哪个并发队列实现?

时间:2009-08-19 18:12:13

标签: java multithreading concurrency queue

来自JavaDocs:

  • 当许多线程共享对公共集合的访问权限时,ConcurrentLinkedQueue是一个合适的选择。此队列不允许使用null元素。
  • ArrayBlockingQueue是一个经典的“有界缓冲区”,其中固定大小的数组包含由生产者插入并由消费者提取的元素。此类支持用于排序等待生产者和消费者线程的可选公平策略
  • LinkedBlockingQueue通常具有比基于阵列的队列更高的吞吐量,但在大多数并发应用程序中的可预测性能更低。

我有2个场景,一个需要队列支持许多生产者(使用它的线程)和一个消费者,另一个是另一种方式。

我不明白要使用哪种实现。有人可以解释一下这些差异是什么吗?

此外,ArrayBlockingQueue中的“可选公平政策”是什么?

6 个答案:

答案 0 :(得分:104)

ConcurrentLinkedQueue表示没有锁定(即没有同步(this)或Lock.lock调用)。它将在修改期间使用CAS - Compare and Swap操作来查看头/尾节点是否仍然与它开始时相同。如果是,则操作成功。如果头/尾节点不同,它会旋转并再试一次。

在进行任何修改之前,

LinkedBlockingQueue将锁定。所以你的报价电话会阻止,直到他们获得锁定。您可以使用带有TimeUnit的商品重载来表示您只愿意在放弃添加之前等待X时间(通常适用于消息类型队列,其中消息在X毫秒后过时)。

公平意味着Lock实现将保持线程的顺序。意味着如果线程A进入然后线程B进入,则线程A将首先获得锁定。没有公平,真的不确定会发生什么。它很可能是下一个被安排的线程。

至于使用哪一个,取决于。我倾向于使用ConcurrentLinkedQueue,因为我的生产者花在工作上的时间是多种多样的。我没有很多生产商在同一时刻生产。但消费者方面更复杂,因为民意调查不会进入良好的睡眠状态。你必须亲自处理。

答案 1 :(得分:44)

基本上它们之间的区别在于性能特征和阻塞行为。

首先采用最简单的方法,ArrayBlockingQueue是一个固定大小的队列。因此,如果将大小设置为10,并尝试插入第11个元素,则insert语句将阻塞,直到另一个线程删除元素。如果多个线程试图同时插入和删除(换句话说在阻塞队列期间)会发生公平性问题。公平算法确保请求的第一个线程是第一个得到的线程。否则,给定线程可能比其他线程等待更长时间,从而导致不可预测的行为(有时一个线程将花费几秒钟,因为之后开始的其他线程首先得到处理)。权衡是管理公平性需要管理费用,从而降低吞吐量。

LinkedBlockingQueueConcurrentLinkedQueue之间最重要的区别在于,如果您从LinkedBlockingQueue请求一个元素并且队列为空,那么您的线程将一直等到那里有一些东西。 ConcurrentLinkedQueue将立即返回空队列的行为。

哪一个取决于您是否需要阻止。你有很多生产者和一个消费者,听起来就像这样。另一方面,如果您有许多消费者而且只有一个生产者,您可能不需要阻止行为,并且可能很高兴让消费者检查队列是否为空并且如果是则继续前进。

答案 2 :(得分:8)

您的问题标题提及阻止队列。但是,ConcurrentLinkedQueue 不是阻塞队列。

BlockingQueueArrayBlockingQueueDelayQueueLinkedBlockingDequeLinkedBlockingQueuePriorityBlockingQueueSynchronousQueue

其中一些显然不适合您的目的(DelayQueuePriorityBlockingQueueSynchronousQueue)。 LinkedBlockingQueueLinkedBlockingDeque是相同的,除了后者是双端队列(它实现了Deque接口)。

由于ArrayBlockingQueue仅在您要限制元素数量时才有用,我会坚持LinkedBlockingQueue

答案 3 :(得分:4)

ArrayBlockingQueue具有较低的内存占用,它可以重用元素节点,而不像LinkedBlockingQueue那样必须为每个新插入创建一个LinkedBlockingQueue $ Node对象。

答案 4 :(得分:1)

  1. SynchronousQueue(取自另一个question
  2. SynchronousQueue更像是一种切换,而LinkedBlockingQueue只允许一个元素。不同之处在于put()SynchronousQueue的调用在相应的take()调用之前不会返回,但LinkedBlockingQueue的大小为put() BlockingQueue调用(到空队列)将立即返回。它本质上是LinkedBlockingQueue实现,当你不想要一个队列时(你不想维护任何未决数据)。

    1. LinkedListLinkedList实现但不完全JDK实现public LinkedBlockingQueue(int capacity) { if (capacity < = 0) throw new IllegalArgumentException(); this.capacity = capacity; last = head = new Node< E >(null); // Maintains a underlying linkedlist. ( Use when size is not known ) } 它使用静态内部类Node来维护元素之间的链接)
    2. LinkedBlockingQueue的构造函数

      static class Node<E> {
          E item;
          Node<E> next;
          Node(E x) { item = x; }
      }
      

      用于维护链接的节点类

      public ArrayBlockingQueue(int capacity, boolean fair) 
      {
                  if (capacity < = 0)
                      throw new IllegalArgumentException();
                  this.items = new Object[capacity]; // Maintains a underlying array
                  lock = new ReentrantLock(fair);
                  notEmpty = lock.newCondition();
                  notFull =  lock.newCondition();
      }
      

      3。 ArrayBlockingQueue(数组实现)

      ArrayBlockingQueue的构造函数

      ArrayBlockingQueue

      恕我直言,LinkedBlockingQueueArrayBlockingQueue之间的最大差异很明显,构造函数1具有基础数据结构数组和其他linkedList

      LinkedBlockingQueue使用single-lock double condition algorithm,而{{1}}是“双锁队列”算法的变体,它有2个锁2个条件(takeLock,putLock)

答案 5 :(得分:0)

ConcurrentLinkedQueue是无锁的,LinkedBlockingQueue则不是。每次调用LinkedBlockingQueue.put()或LinkedBlockingQueue.take()时,都需要先获取锁。换句话说,LinkedBlockingQueue的并发性很差。如果您关心性能,请尝试使用ConcurrentLinkedQueue + LockSupport。