Java BlockingQueue с дозированием?

меня интересует структура данных, идентичная Java BlockingQueue, за исключением того, что она должна иметь возможность паковать объекты в очереди. Другими словами, Я хотел бы, чтобы производитель мог помещать объекты в очередь, но имел потребительский блок на take() пока очередь не достигнет определенного размера (размер пакета).

затем, как только очередь достигла размера партии, производитель должен заблокироватьput() пока потребитель потребил все элементы в очередь (в этом случае производитель начнет производство снова, а потребительский блок-до тех пор, пока партия не будет достигнута снова).

существует ли аналогичная структура данных? Или я должен написать это (что я не против), я просто не хочу тратить свое время, если там что-то есть.


обновление

возможно, чтобы немного прояснить ситуацию:

ситуация всегда будет следующим. Может быть несколько производителей, добавляющих элементы в очередь, но никогда не будет больше одного потребителя, принимающего элементы из очереди.

теперь проблема в том, что существует несколько этих настроек параллельно и последовательно. Иными словами, производители производят товары для нескольких очередей, а потребители сами могут быть производителями. Это легче представить как направленный график производителей, потребителей-производителей и, наконец, потребителей.

причина, по которой производители должны блокировать, пока очереди не опустеют (@Peter Lawrey) потому что каждый из них будет работать в потоке. Если вы оставите их просто производить по мере того, как пространство становится доступным, вы закончите с ситуацией, когда у вас будет слишком много потоков, пытающихся обработать слишком много вещей одновременно.

может быть, соединение этого со службой выполнения может решить проблему?

4 ответов


Я бы предложил вам использовать интерфейса blockingqueue.drainTo (Collection, int). Вы можете использовать его с take (), чтобы получить минимальное количество элементов.

преимущество использования этого подхода заключается в том, что размер пакета динамически растет с рабочей нагрузкой, и производителю не нужно блокировать, когда потребитель занят. т. е. он сам оптимизирует задержку и пропускную способность.


чтобы реализовать точно так, как спросил (что я думаю, это плохая идея) , вы можете использовать SynchronousQueue с занятым потребляющим потоком.

т. е. потребляющий поток делает

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

производитель будет блокировать, когда потребитель занят.


вот быстрая (=простая, но не полностью протестированная) реализация, которая, я думаю, может быть подходящей для ваших запросов - вы должны иметь возможность расширить ее для поддержки полного интерфейса очереди, если вам нужно.

для повышения производительности вы можете переключиться на 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;
    }

}

надеюсь, вы найдете его полезным.


Не то, что я знаю. Если я правильно понимаю, вы хотите, чтобы производитель работал (пока потребитель заблокирован), пока он не заполнит очередь, или потребитель работал (пока производитель блокирует), пока он не очистит очередь. Если это так, я могу предположить, что вам не нужна структура данных, а механизм блокировки одной стороны, пока другая работает в режиме мьютекса. Вы можете заблокировать объект для этого и внутренне иметь логику полного или пустого освобождения замок и передать его другой стороне. Короче говоря, вы должны написать это сами:)


Это похоже на то, как работает RingBuffer в шаблоне Разрушителя LMAX. См.http://code.google.com/p/disruptor/ Для больше.

очень грубое объяснение - ваша основная структура данных-это RingBuffer. Производители помещают данные в кольцевой буфер последовательно, и потребители могут извлечь столько данных, сколько производитель поместил в буфер (так что по существу дозирование). Если буфер заполнен, производитель блокирует, пока потребитель не закончит и не освободит слоты в буфер.