Java 带水印的阻塞队列

Java 带水印的阻塞队列,java,multithreading,concurrency,queue,Java,Multithreading,Concurrency,Queue,我面临一个生产者/消费者问题,我想优化我的解决方案 当前的解决方案是使用阻塞队列,它基本上使生产者在队列已满时等待,并在队列上有可用空间时将其唤醒。对于使用者,它将等待使使用者在空时等待,并在向队列中添加新内容时将其唤醒 我实现的另一个自定义解决方案是使用水印,当生产者线程向队列提供数据时,队列达到高水位线时,它们将处于“放置”状态,只有在达到低水位线时才会恢复 是否已经在某个地方实现过类似的事情? < P>你是否考虑过 LinkedTransferQueue < /代码>,这使得生产者等待

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

当前的解决方案是使用阻塞队列,它基本上使生产者在队列已满时等待,并在队列上有可用空间时将其唤醒。对于使用者,它将等待使使用者在空时等待,并在向队列中添加新内容时将其唤醒

我实现的另一个自定义解决方案是使用水印,当生产者线程向队列提供数据时,队列达到高水位线时,它们将处于“放置”状态,只有在达到低水位线时才会恢复


<强>是否已经在某个地方实现过类似的事情?

< P>你是否考虑过<代码> LinkedTransferQueue < /代码>,这使得生产者等待一个消费者准备接收?我认为这将防止队列长度意外变长…

我不确定您是否真的需要除
阻塞队列之外的其他解决方案,因为您还可以在批量将元素放入队列时使用它

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

我也有类似的需求(在不同的背景下)。如果您的系统不需要极高的性能,您可以将
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();
    }
}
import java.util.AbstractQueue;
导入java.util.ArrayDeque;
导入java.util.Iterator;
导入java.util.Queue;
导入java.util.concurrent.AtomicBoolean;
公共最终类WatermarkQueue扩展了AbstractQueue{
private final Queue=new ArrayDeque();
lowerWatermark私人终审法院;
私有最终水印;
私人最终运行暂停;
私人最终可运行的恢复;
private final AtomicBoolean ProducersSuspended=新的AtomicBoolean(false);
公共水印队列(int lowerWatermark、int upperWatermark、Runnable onSuspend、Runnable onResume){
this.lowerWatermark=lowerWatermark;
this.upperWatermark=upperWatermark;
this.onSuspend=onSuspend;
this.onResume=onResume;
}
@凌驾
公共迭代器迭代器(){
抛出新的非法状态异常(“未实现”);
}
@凌驾
公共同步整数大小(){
返回queue.size();
}
@凌驾
公共同步布尔报价(E){
最后一个布尔值waspempy=queue.isEmpty();
排队。报价(e);
if(queue.size()==upperWatermark){
如果(!producerSuspended.getAndSet(true)){
onSuspend.run();
}
}
如果(为空){
queue.notify();
}
返回true;
}
@凌驾
公共电子投票(){
while(queue.isEmpty()){
试一试{
queue.wait();
}捕捉(中断异常e){
Thread.currentThread().interrupt();
返回null;
}
}
final E=queue.poll();
如果(e!=null){
if(queue.size()==lowerWatermark){
if(producerSuspended.getAndSet(false)){
onResume.run();
}
}
}
返回e;
}
@凌驾
公共同步E peek(){
return queue.peek();
}
}

那么您想要对
阻塞队列进行流量控制
?队列满/空时阻塞与队列几乎满/空时阻塞有什么区别?当然,这将允许您执行突发检索/插入,但这实际上会影响代码的性能吗?其想法是减少对producer数据源的轮询,我将能够使用更大的结果集执行更少的查询。啊,因此producer是数据库驱动的,这是有意义的。消息队列可能有一些与此相关的功能,但如果您已经有了自定义解决方案,我不确定您是否要为此添加消息队列。我假设您的自定义实现使用
remainingCapacity()
来控制流量?你的工作情况如何,你能测试性能吗?据我所知,这是一个延迟问题?我的意思是,这就是水印将要做的——提前阅读,这样消费者就永远不会被阻止或在更短的时间内被阻止?为了减少读取,您可以简单地增加队列和获取大小?感谢您的提示,我会检查它,也许专门化它会解决我的问题。缓冲区的好主意,但队列是有限的,因为有了缓冲区,我会在堆中的某个地方有其他N元素,直到队列可以接受它们
// 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); 
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();
    }
}