首页 文章

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

提问于
浏览
112

来自JavaDocs:

  • 当许多线程共享对公共集合的访问权时,ConcurrentLinkedQueue是一个合适的选择 . 此队列不允许null元素 .

  • ArrayBlockingQueue是一个经典的"bounded buffer",其中固定大小的数组包含由 生产环境 者插入并由消费者提取的元素 . 此类支持用于排序等待 生产环境 者和消费者线程的可选公平策略

  • LinkedBlockingQueue通常具有比基于阵列的队列更高的吞吐量,但在大多数并发应用程序中具有较低的可预测性能 .

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

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

另外, ArrayBlockingQueue 中的'optional fairness policy'是什么?

6 回答

  • 7

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

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

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

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

  • 41

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

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

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

    至于哪一个使用,取决于 . 我倾向于使用ConcurrentLinkedQueue因为我的 生产环境 者花时间投入队列的时间是多种多样的 . 我没有进入一个良好的睡眠状态 . 你必须自己处理 .

  • 103

    你的问题 Headers 提到阻止队列 . 但是, ConcurrentLinkedQueuenot 阻塞队列 .

    BlockingQueue s是 ArrayBlockingQueueDelayQueueLinkedBlockingDequeLinkedBlockingQueuePriorityBlockingQueueSynchronousQueue .

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

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

  • 3

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

  • 1
    • SynchronousQueue (取自另一个question

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

    • LinkedBlockingQueueLinkedList 实现但并非完全JDK实现 LinkedList 它使用静态内部类Node来维护元素之间的链接)

    LinkedBlockingQueue的构造函数

    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 )
    }
    

    用于维护链接的节点类

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

    3 . ArrayBlockingQueue(数组实现)

    ArrayBlockingQueue的构造函数

    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();
    }
    

    恕我直言 ArrayBlockingQueueLinkedBlockingQueue 之间最大的区别很明显,构造函数有 underlying data structure Array and other linkedList .

    ArrayBlockingQueue 使用single-lock double condition algorithmLinkedBlockingQueue 是"two lock queue"算法的变体,它有2个锁2个条件(takeLock,putLock)

  • 0

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

相关问题