带批处理的Java BlockingQueue?

带批处理的Java BlockingQueue?,java,queue,producer-consumer,blockingqueue,Java,Queue,Producer Consumer,Blockingqueue,我对与Java BlockingQueue相同的数据结构感兴趣,但它必须能够批处理队列中的对象。换句话说,我希望生产者能够将对象放入队列,但让消费者块打开take(),直到队列达到某个大小(批量大小) 然后,一旦队列达到批量大小,生产者必须阻塞put(),直到消费者消耗了队列中的所有元素(在这种情况下,生产者将再次开始生产,消费者阻塞,直到再次到达批量) 是否存在类似的数据结构?或者我应该写它(我不介意),我只是不想浪费我的时间,如果有什么东西在那里 更新 也许要澄清一点: 情况总是这样。可以

我对与Java BlockingQueue相同的数据结构感兴趣,但它必须能够批处理队列中的对象。换句话说,我希望生产者能够将对象放入队列,但让消费者块打开
take()
,直到队列达到某个大小(批量大小)

然后,一旦队列达到批量大小,生产者必须阻塞
put()
,直到消费者消耗了队列中的所有元素(在这种情况下,生产者将再次开始生产,消费者阻塞,直到再次到达批量)

是否存在类似的数据结构?或者我应该写它(我不介意),我只是不想浪费我的时间,如果有什么东西在那里


更新

也许要澄清一点:

情况总是这样。可以有多个生产者向队列中添加项目,但决不会有多个消费者从队列中获取项目

现在,问题是这些设置中有多个是并行和串行的。换句话说,生产者为多个队列生产商品,而消费者本身也可以是生产者。这更容易被认为是生产者、消费者生产者和最终消费者的有向图

生产者应该阻塞队列直到队列为空(@Peter Lawrey)的原因是,每个队列都将在一个线程中运行。如果您让它们只是在空间可用时进行生产,那么最终会出现这样一种情况:有太多线程试图同时处理太多的事情


也许将其与执行服务结合可以解决问题?

我不知道。如果我理解正确,您希望生产者工作(当消费者被阻塞时),直到它填满队列,或者消费者工作(当生产者阻塞时),直到它清除队列。如果是这样,我建议您不需要数据结构,而是需要一种机制来阻止一方,而另一方以互斥方式工作。您可以为此锁定一个对象,并在内部拥有“满”或“空”的逻辑,以释放锁并将其传递给另一方。总之,你应该自己写:)

我建议你使用。您可以将它与take()一起使用,以确保获得最少数量的元素

使用这种方法的优点是,批处理大小随工作负载动态增长,并且当消费者忙时,生产者不必阻止。i、 e.它可以自我优化延迟和吞吐量


要完全按照要求实现(我认为这是一个坏主意),您可以使用带有繁忙消费线程的SynchronousQueue

i、 e.消费线程执行以下操作:

 list.clear();
 while(list.size() < required) list.add(queue.take());
 // process list.
list.clear();
while(list.size()

当消费者忙时,生产者会阻止。

这听起来像是在LMAX中断器模式中RingBuffer是如何工作的。更多信息,请参阅

一个非常粗略的解释是,您的主要数据结构是RingBuffer。生产者按顺序将数据放入环形缓冲区,消费者可以提取生产者放入缓冲区的数据量(本质上是批处理)。如果缓冲区已满,生产者将阻止,直到使用者完成并释放缓冲区中的插槽。

这里有一个快速(=简单但未经过充分测试)实现,我认为可能适合您的请求-如果需要,您应该能够扩展它以支持完整队列接口

为了提高性能,您可以切换到ReentrantLock,而不是使用“synchronized”关键字

public class BatchBlockingQueue<T> {

    private ArrayList<T> queue;
    private Semaphore readerLock;
    private Semaphore writerLock;
    private int batchSize;

    public BatchBlockingQueue(int batchSize) {
        this.queue = new ArrayList<>(batchSize);
        this.readerLock = new Semaphore(0);
        this.writerLock = new Semaphore(batchSize);
        this.batchSize = batchSize;
    }

    public synchronized void put(T e) throws InterruptedException {
        writerLock.acquire();
        queue.add(e);
        if (queue.size() == batchSize) {
            readerLock.release(batchSize);
        }
    }

    public synchronized T poll() throws InterruptedException {
        readerLock.acquire();
        T ret = queue.remove(0);
        if (queue.isEmpty()) {
            writerLock.release(batchSize);
        }
        return ret;
    }

}
公共类BatchBlockingQueue{
私有数组列表队列;
专用信号量读取器锁;
专用信号量写锁;
私有整数批量大小;
公共BatchBlockingQueue(int batchSize){
this.queue=新的ArrayList(batchSize);
this.readerLock=新信号量(0);
this.writerLock=新信号量(batchSize);
this.batchSize=batchSize;
}
公共同步的void put(te)抛出InterruptedException{
writerLock.acquire();
添加(e);
if(queue.size()==batchSize){
readerLock.release(批量大小);
}
}
public synchronized T poll()引发InterruptedException{
readerLock.acquire();
T ret=queue.remove(0);
if(queue.isEmpty()){
writerLock.release(批大小);
}
返回ret;
}
}

希望您觉得它有用。

我最近开发了一个实用程序,如果队列元素未达到批处理大小,则使用刷新超时来批处理阻塞队列元素。它还支持扇出模式,使用多个实例来阐述同一组数据:

// Instantiate the registry
FQueueRegistry registry = new FQueueRegistry();

// Build FQueue consumer
registry.buildFQueue(String.class)
                .batch()
                .withChunkSize(5)
                .withFlushTimeout(1)
                .withFlushTimeUnit(TimeUnit.SECONDS)
                .done()
                .consume(() -> (broadcaster, elms) -> System.out.println("elms batched are: "+elms.size()));

// Push data into queue
for(int i = 0; i < 10; i++){
        registry.sendBroadcast("Sample"+i);
}
//实例化注册表
FQueueRegistry注册表=新FQUEUEUERegistry();
//构建FQUE用户
registry.buildFQUE(String.class)
.batch()
.withChunkSize(5)
.withFlushTimeout(1)
.带FlushTimeUnit(时间单位秒)
.完成
.consume(()->(广播公司,elms)->System.out.println(“批处理的elms为:+elms.size()));
//将数据推送到队列中
对于(int i=0;i<10;i++){
registry.sendBroadcast(“示例”+i);
}
更多信息在这里


我希望生产者在消费者忙的时候阻止。有趣的是,大多数系统都会竭尽全力避免这种情况发生。;)第二个建议正好可以做到这一点。如果您希望生产者阻止,为什么要使用多个线程?如果“生产者”是处理器/消费者,而您似乎不希望它们同时运行,这不是更简单吗?请查看我的更新。该设计要求生成程序块过大,因此执行线程的数量保持较低。此外,它还解决了一个依赖性问题