Где вы должны использовать реализации BlockingQueue вместо простых реализаций очереди? - PullRequest
20 голосов
/ 11 декабря 2008

Я думаю, что я перефразирую свой вопрос от

Где следует использовать реализации BlockingQueue вместо простых реализаций очереди?

до

Каковы преимущества / недостатки BlockingQueue по сравнению с реализациями Queue, принимая во внимание такие аспекты, как скорость, параллелизм или другие свойства, которые варьируются, например время доступа к последнему элементу.

Я использовал оба вида очередей. Я знаю, что очередь блокировки обычно используется в параллельном приложении. Я писал простой пул ByteBuffer, в котором мне нужно было заполнить объекты ByteBuffer. Мне нужна была самая быстрая, многопоточная реализация очереди. Даже есть реализации List, такие как ArrayList, который имеет постоянное время доступа к элементам.

Кто-нибудь может обсудить плюсы и минусы реализации BlockingQueue против Queue против List?

В настоящее время я использовал ArrayList для хранения этих объектов ByteBuffer.

Какую структуру данных я буду использовать для хранения этих объектов?

Ответы [ 4 ]

31 голосов
/ 11 декабря 2008

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

В подобных ситуациях может быть лучше сообщить потенциальному производителю, что очередь заполнена, и быстро отказаться от ошибки. Например, источником может быть веб-запрос с пользователем, который не хочет ждать слишком долго, и хотя он не будет потреблять много циклов ЦП во время ожидания, он использует ограниченные ресурсы, такие как сокет и некоторое количество памяти. , Отказ предоставит поставленным в очередь задачам лучший шанс для своевременного завершения.


Относительно измененного вопроса, который я интерпретирую как «Что такое хорошая коллекция для хранения объектов в пуле?»

Неограниченный LinkedBlockingQueue - хороший выбор для многих пулов. Однако, в зависимости от вашей стратегии управления пулом, ConcurrentLinkedQueue может также работать.

В приложении для пула блокирующий «пут» не подходит. Управление максимальным размером очереди является задачей менеджера пула - он решает, когда создавать или уничтожать ресурсы для пула. Клиенты пула заимствуют и возвращают ресурсы из пула. Добавление нового объекта или возврат ранее заимствованного объекта в пул должны быть быстрыми, не блокирующими операциями. Таким образом, очередь с ограниченной емкостью не подходит для пулов.

С другой стороны, при извлечении объекта из пула большинству приложений требуется ждать, пока ресурс не станет доступен. Операция «take», которая блокирует, по крайней мере временно, гораздо более эффективна, чем «ожидание занятости» - многократный опрос, пока ресурс не станет доступным. LinkedBlockingQueue - хороший выбор в этом случае. Заемщик может заблокировать на неопределенный срок с помощью take или ограничить время, которое он желает заблокировать, с помощью poll.

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

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

3 голосов
/ 11 декабря 2008

Вы увидите BlockingQueue в многопоточных ситуациях. Например, вам нужно передать BlockingQueue в качестве параметра для создания ThreadPoolExecutor, если вы хотите создать его с помощью конструктора. В зависимости от типа очереди, которую вы передаете исполнителю, может действовать по-разному.

1 голос
/ 21 августа 2017

Это Queue реализация, которая дополнительно поддерживает операции,

ждать, пока очередь не станет пустой при извлечении элемента,

и

ждать, пока место в очереди станет доступным при хранении элемент.

Если вам требуется указанная выше функциональность, за ней следует ваша реализация Queue, тогда используйте Blocking Queue

0 голосов
/ 19 апреля 2017

BlockingQueue также полезно для сериализации параллельных операций, которые зависят друг от друга.

Чтобы привести конкретный (хотя и несколько произвольный) пример, здесь - это параллельный тест для веб-приложения очереди пациентов в реальном времени, где callInPatient() должен запускаться параллельно с registerPatientToAppointment(), но требует дождаться завершения registerPatientToAppointment() перед выполнением callPatientInAtDoctorsOffice():

public class ClinicPatientQueueAppTest extends ParallelTest {

    private static final BlockingQueue WAIT_FOR_REGISTRATION_QUEUE = new ArrayBlockingQueue(2);

    @Test
    public void callInPatient() {
        loginToDoctorsOffice("user", "password");
        waitUntilRegistrationCompleted();  // <--
        callPatientInAtDoctorsOffice();
    }

    @Test
    public void registerPatientToAppointment() {
        registerPatientAtRegistrationKiosk("Patient Peter");
        notifyRegistrationCompleted(); // <--
    }

    private void waitUntilRegistrationCompleted() {
        WAIT_FOR_REGISTRATION_QUEUE.take();
    }

    private void notifyRegistrationCompleted() {
        WAIT_FOR_REGISTRATION_QUEUE.put(this);
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...