用水印阻止队列

时间:2014-07-29 07:45:00

标签: java multithreading concurrency queue

我正面临生产者/消费者问题,我想优化我的解决方案。

当前的解决方案是使用阻塞队列,它基本上使生产者在队列满时等待,并在队列中有可用空间时将其唤醒。对于消费者而言,它将等待消费者在空的时候等待,并在将新的东西添加到队列中时将其唤醒。

我实现的另一个自定义解决方案是使用水印,当生产者线程进入队列并且它达到高水位时它们被置于状态,并且它们将仅在达到低水印时恢复。

是否已在某处实施类似内容?

3 个答案:

答案 0 :(得分:1)

您是否考虑过让生产者等待准备接收的消费者的LinkedTransferQueue?我认为这可以防止队列长度意外地变长......

答案 1 :(得分:1)

我不确定你是否真的需要另一种解决方案而不是BlockingQueue,因为当你想要将元素分批放入队列时也可以使用它。

那是因为您可以将数据源查询到内部缓冲区,然后将缓冲区复制到队列中(当然可能会阻塞):

// Producer's main loop
List<E> buffer;
do {
    // initialize buffer
    buffer = new ArrayList<E>(batchSize);

    // read batchSize elements from data source into buffer
    read(buffer, batchSize);

    // put all elements into queue
    for (E element : buffer) {
        queue.put(element); // may block if queue full
    }
} while (buffer.size() == batchSize); // until less than batchSize read
queue.put(poisonPill); // some special element to communicate end-of-data

同样的策略可以在消费者方面使用!您可以先从队列中取batchSize个元素(这可能会阻塞直到有足够的数据可用),而不是单个元素,然后处理内部缓冲区:

// Consumer's main loop
List<E> buffer;
do {
    // initialize buffer
    buffer = new ArrayList<E>(batchSize);

    int i = 0;
    E element;

    // take batchSize elements from queue; stop if poisonPill found
    while (i++ < batchSize && !(element = queue.take()).equals(poisonPill)) {
        buffer.add(element);
    }

    // process buffer
    process(buffer);

    // until less than batchSize read
} while (buffer.size() == batchSize); 

答案 2 :(得分:0)

我有类似的需求(在不同的背景下)。如果您的系统不需要极高的性能,您只需使用ArrayDeque同步。

import java.util.AbstractQueue;
import java.util.ArrayDeque;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;

public final class WatermarkQueue<E> extends AbstractQueue<E> {
    private final Queue<E> queue = new ArrayDeque<>();

    private final int lowerWatermark;
    private final int upperWatermark;

    private final Runnable onSuspend;
    private final Runnable onResume;

    private final AtomicBoolean producerSuspended = new AtomicBoolean(false);

    public WatermarkQueue(int lowerWatermark, int upperWatermark, Runnable onSuspend, Runnable onResume) {
        this.lowerWatermark =lowerWatermark;
        this.upperWatermark = upperWatermark;
        this.onSuspend = onSuspend;
        this.onResume = onResume;
    }

    @Override
    public Iterator<E> iterator() {
        throw new IllegalStateException("not implemented");
    }

    @Override
    public synchronized int size() {
        return queue.size();
    }

    @Override
    public synchronized boolean offer(E e) {
        final boolean wasEmpty = queue.isEmpty();
        queue.offer(e);

        if (queue.size() == upperWatermark) {
            if (!producerSuspended.getAndSet(true)) {
                onSuspend.run();
            }
        }

        if (wasEmpty) {
            queue.notify();
        }

        return true;
    }

    @Override
    public synchronized E poll() {
        while (queue.isEmpty()) {
            try {
                queue.wait();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            }
        }

        final E e = queue.poll();
        if (e != null) {
            if (queue.size() == lowerWatermark) {
                if (producerSuspended.getAndSet(false)) {
                    onResume.run();
                }
            }
        }

        return e;
    }

    @Override
    public synchronized E peek() {
        return queue.peek();
    }
}