Как использовать ConcurrentLinkedQueue? - PullRequest
85 голосов
/ 05 марта 2009

Как использовать ConcurrentLinkedQueue в Java?
Используя это LinkedQueue, нужно ли беспокоиться о параллелизме в очереди? Или мне просто нужно определить два метода (один для извлечения элементов из списка, а другой для добавления элементов в список)?
Примечание: очевидно, что эти два метода должны быть синхронизированы. Правильно?


РЕДАКТИРОВАТЬ: Я пытаюсь сделать следующее: у меня есть класс (на Java) с одним методом для извлечения элементов из очереди и другой класс с одним методом для добавления элементов в очередь , Элементы, добавленные и извлеченные из списка, являются объектами моего собственного класса.

Еще один вопрос: мне нужно сделать это в методе удаления:

while (queue.size() == 0){ 
  wait(); 
  queue.poll();
}

У меня только один потребитель и один производитель.

Ответы [ 6 ]

146 голосов
/ 05 марта 2009

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

Сначала создайте свою очередь:

Queue<YourObject> queue = new ConcurrentLinkedQueue<YourObject>();

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

YourProducer producer = new YourProducer(queue);

и

YourConsumer consumer = new YourConsumer(queue);

и добавьте к нему материал у вашего производителя:

queue.offer(myObject);

и извлеките содержимое у вашего потребителя (если очередь пуста, poll () вернет ноль, поэтому проверьте это):

YourObject myObject = queue.poll();

Для получения дополнительной информации см. Javadoc

.

EDIT:

Если вам нужно заблокировать ожидание, пока очередь не станет пустой, вы, вероятно, захотите использовать LinkedBlockingQueue и использовать метод take (). Однако LinkedBlockingQueue имеет максимальную емкость (по умолчанию Integer.MAX_VALUE, которая превышает два миллиарда) и, таким образом, может быть или не быть подходящей в зависимости от ваших обстоятельств.

Если у вас есть только один поток, помещающий материал в очередь, а другой поток, удаляющий материал из очереди, ConcurrentLinkedQueue, вероятно, излишний. Это больше для случаев, когда у вас могут быть сотни или даже тысячи потоков, обращающихся к очереди одновременно. Ваши потребности, вероятно, будут удовлетворены с помощью:

Queue<YourObject> queue = Collections.synchronizedList(new LinkedList<YourObject>());

Плюсом этого является то, что он блокирует экземпляр (очередь), поэтому вы можете синхронизировать очередь, чтобы обеспечить атомарность составных операций (как объяснил Джаред). Вы НЕ МОЖЕТЕ делать это с ConcurrentLinkedQueue, поскольку все операции выполняются БЕЗ блокировки на экземпляре (с использованием переменных java.util.concurrent.atomic). Вам НЕ нужно будет делать это, если вы хотите заблокировать, когда очередь пуста, потому что poll () просто вернет ноль, пока очередь пуста, а poll () является атомарным. Проверьте, возвращает ли poll () значение NULL. Если это так, подождите (), затем попробуйте снова. Не нужно блокировать.

Наконец:

Честно говоря, я бы просто использовал LinkedBlockingQueue. Это все еще излишне для вашего приложения, но, скорее всего, оно будет работать нормально. Если он недостаточно эффективен (ПРОФИЛЬ!), Вы всегда можете попробовать что-то еще, а это значит, что вам не нужно иметь дело с ЛЮБОЙ синхронизированной информацией:

BlockingQueue<YourObject> queue = new LinkedBlockingQueue<YourObject>();

queue.put(myObject); // Blocks until queue isn't full.

YourObject myObject = queue.take(); // Blocks until queue isn't empty.

Все остальное тоже самое. Положите , вероятно не будет блокировать, потому что вы вряд ли поместите два миллиарда объектов в очередь.

33 голосов
/ 06 марта 2009

Это в значительной степени дубликат другого вопроса .

Вот раздел этого ответа, который имеет отношение к этому вопросу:

Нужно ли выполнять собственную синхронизацию, если я использую java.util.ConcurrentLinkedQueue?

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

Например, это потокобезопасно без каких-либо действий с вашей стороны:

queue.add(obj);

или

queue.poll(obj);

Тем не менее, неатомарные вызовы в очередь не являются автоматически поточно-ориентированными. Например, следующие операции не автоматически поточно-ориентированы:

if(!queue.isEmpty()) {
   queue.poll(obj);
}

Этот последний не является потокобезопасным, поскольку вполне возможно, что между временем вызова isEmpty и вызовом опроса времени другие потоки будут добавлять или удалять элементы из очереди. Потокобезопасный способ сделать это так:

synchronized(queue) {
    if(!queue.isEmpty()) {
       queue.poll(obj);
    }
}

Опять же ... атомарные вызовы в очередь автоматически поточно-ориентированы. Неатомарные звонки - нет.

6 голосов
/ 25 октября 2012

Это, вероятно, то, что вы ищете с точки зрения безопасности потоков и "симпатичности" при попытке использовать все в очереди:

for (YourObject obj = queue.poll(); obj != null; obj = queue.poll()) {
}

Это гарантирует, что вы выйдете, когда очередь пуста, и продолжите выталкивать из нее объекты, пока она не пуста.

6 голосов
/ 05 марта 2009

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

2 голосов
/ 06 марта 2009

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

1 голос
/ 05 марта 2009

Просто используйте его, как если бы вы использовали не одновременную коллекцию. Классы Concurrent [Collection] оборачивают обычные коллекции, так что вам не нужно думать о синхронизации доступа.

Редактировать: ConcurrentLinkedList на самом деле не просто оболочка, а, скорее, лучшая параллельная реализация. В любом случае вам не нужно беспокоиться о синхронизации.

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