我正面临生产者/消费者问题,我想优化我的解决方案。
当前的解决方案是使用阻塞队列,它基本上使生产者在队列满时等待,并在队列中有可用空间时将其唤醒。对于消费者而言,它将等待消费者在空的时候等待,并在将新的东西添加到队列中时将其唤醒。
我实现的另一个自定义解决方案是使用水印,当生产者线程进入队列并且它达到高水位时它们被置于状态,并且它们将仅在达到低水印时恢复。
是否已在某处实施类似内容?
答案 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();
}
}