Подтвердить что ты не робот

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

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

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

to

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

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

Может ли кто-нибудь обсудить преимущества и недостатки реализации BlockingQueue vs Queue vs List?

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

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

4b9b3361

Ответ 1

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

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


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

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

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

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

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

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

Ответ 2

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

Ответ 3

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

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

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);
    }

}

Ответ 4

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

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

и

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

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