LinkedBlockingQueue .take()表现

时间:2013-06-19 14:24:03

标签: java multithreading

我正在使用被多个线程填充的LinkedBlockingQueue,项目数量很大(数千万个对象)。

LinkedBlockingQueue.take()需要花费大量时间(通过分析器检查) - 56%的时间 队列永远不会是空的!!

什么会影响take()方法的效果?

更新: 我在处理take()结果的代码中做了一些更改,我还将take()放到另一个线程中,性能几乎提高了50%。

我不明白这是怎么可能的,因为我没有改变推杆的任何逻辑......

更新:

我在调用take()之前计算了队列已满的次数:
使用原始代码,90%的呼叫队列已满 通过改进的代码,13%的呼叫队列已满。

2 个答案:

答案 0 :(得分:5)

take()的重量相当轻,但是如果你调用它就会消耗大量的CPU。听起来你传递了大量的物品,这些物品对于消费者来说需要的工作量非常小。我建议尝试重构你的问题,以便更有效地完成。

例如,您可以

  • 使用专为此类问题设计的Disruptor。
  • 发送块/批数据,例如一个对象列表而不是许多单个对象。
  • 使用多个交换器(适用于少数作者)
  • 如果您的目标是保留数据,请使用像Chronicle这样的内容。

您的个人资料也可能不完全准确。当你测量很小的时间段时,它会产生混合的结果。

BTW:take()是单线程的。如果你有许多线程试图同时调用take(),它们将互相阻塞。

答案 1 :(得分:5)

如果我们查看代码here,我们可以看到在获取元素之前必须进行锁定。如果有很多线程正在进行,那么这个锁就会存在争用 - 线程不会等待出现的东西,而是让其他线程可以处理。

 public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            try {
                while (count.get() == 0)
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to a non-interrupted thread
                throw ie;
            }

            x = extract();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

修改

如果您有一个接受者和许多推杆并且队列不断变满,则此代码将给出signalNotFull()瓶颈

private void signalNotFull() {
    final ReentrantLock putLock = this.putLock;
    putLock.lock();
    try {
        notFull.signal();
    } finally {
        putLock.unlock();
    }
}

这需要putLock表示队列中现在有空间这一事实。