消费者 - 生产者争用率高

时间:2015-03-23 18:10:13

标签: java multithreading collections producer-consumer

我想知道实现多个生产者的最佳机制是什么 - 单个消费者场景,我必须保持当前未处理请求的数量是最新的。

我的第一个想法是使用ConcurrentLinkedQueue:

public class SomeQueueAbstraction {

    private Queue<SomeObject> concurrentQueue = new ConcurrentLinkedQueue<>();
    private int size;

    public void add(Object request) {
        SomeObject object = convertIncomingRequest(request);   
        concurrentQueue.add(object);
        size++;

    }

    public SomeObject getHead() {
        SomeObject object = concurrentQueue.poll();
        size--;
    }

    // other methods

问题在于我必须在addsize ++以及pollsize--上明确同步,才能始终保持准确{{1}这使得size开始时毫无意义。

在保持数据一致性的同时,实现尽可能好的性能的最佳方法是什么?

我应该使用ConccurentLinkedQueue代替显式同步还是有更好的方法来实现这一目标?

这里有类似的问题/答案:

java.util.ConcurrentLinkedQueue

讨论ArrayDequeue上的复合操作如何自然不是原子的,但没有直接的答案,给定场景的最佳选择是什么。

注意:我正在明确计算大小,因为固有的.size()方法的时间复杂度为O(n)。

注意2:我也担心我没有明确写过的getSize()方法会增加更多的争用开销。可以相对频繁地调用它。

我正在寻找处理多个生产者的最有效方法 - 单个使用频繁的getSize()调用的消费者。

替代建议:如果SomeObject结构中有elementId,我可以从ConcurrentLinkedQueue.poll()获得当前大小,并且只需要在机制内完成锁定以生成这样的id。现在可以正确使用添加和获取而无需额外锁定。这种票价如何作为替代品呢?

2 个答案:

答案 0 :(得分:1)

您可以使用显式锁定,这意味着您可能不需要并发队列。

public class SomeQueueAbstraction {

    private Queue<SomeObject> queue = new LinkedList<>();
    private volatile int size;
    private Object lock = new Object();

    public void add(Object request) {
        SomeObject object = convertIncomingRequest(request); 
        synchronized(lock) {  
            queue.add(object);
            size++;
        }
    }

    public SomeObject getHead() {
        SomeObject object = null;
        synchronized(lock) {
            object = queue.poll();
            size--;
        }
        return object;
    }

    public int getSize() {
        synchronized(lock) {
            return size;
        }
    }

    // other methods
}

这样,就可以安全地在队列中添加/删除元素并更新size

答案 1 :(得分:1)

因此要求是报告最新的当前未处理请求数。这经常被要求确实使ConcurrentLinkedQueue.size()不合适。

这可以使用AtomicInteger来完成:它很快,并且总是尽可能接近当前未处理的请求数。

以下是一个示例,请注意一些小更新,以确保报告的大小准确无误:

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

public class SomeQueueAbstraction {

    private final Queue<SomeObject> concurrentQueue = new ConcurrentLinkedQueue<>();
    private final AtomicInteger size = new AtomicInteger();

    public boolean add(Object request) {

        SomeObject object = convertIncomingRequest(request);   
        if (concurrentQueue.add(object)) {
            size.incrementAndGet();
            return true;
        }
        return false;
    }

    public SomeObject remove() {

        SomeObject object = concurrentQueue.poll();
        if (object != null) {
            size.decrementAndGet();
        }
        return object;
    }

    public int getSize() { return size.get(); }

    private SomeObject convertIncomingRequest(Object request) { 
        return new SomeObject(getSize()); 
    }

    class SomeObject {
        int id;
        SomeObject(int id) { this.id = id; }
    }
}