Як користуватися ConcurrentLinkedQueue?


95

Як я можу використовувати ConcurrentLinkedQueueJava?
Використовуючи це LinkedQueue, чи потрібно мені турбуватися про паралельність у черзі? Або мені просто потрібно визначити два способи (один для отримання елементів зі списку та інший для додавання елементів до списку)?
Примітка: очевидно, що ці два методи мають бути синхронізовані. Правильно?


EDIT: Я намагаюся зробити так: у мене є клас (на Java) з одним методом для отримання елементів із черги, а інший клас з одним методом для додавання елементів до черги. Елементи, додані та отримані зі списку, є об'єктами мого власного класу.

Ще одне питання: чи потрібно це робити методом видалення:

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

У мене є лише один споживач і один виробник.


Дякую за відповідь на питання mt. Я намагаюся зробити це: у мене є клас (на Java) з одним методом для отримання елементів з черги та інший клас з одним методом для додавання елементів у чергу. Елементи, додані та отримані зі списку, є об'єктами мого власного класу.
Рікардо Фелгейрас

2
Ви повинні відредагувати своє запитання та ввести це роз’яснення у саме запитання.
Адам Яскевич

Відповіді:


156

Ні, методи не потрібно синхронізувати, і вам не потрібно визначати будь-які методи; вони вже знаходяться в 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

Редагувати:

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

Якщо у вас є лише один потік, який розміщує речі в черзі, а інший потік виводить речі з черги, ConcurrentLinkedQueue, ймовірно, надмірний. Це більше для того, коли у вас можуть бути сотні або навіть тисячі потоків, що одночасно отримують доступ до черги. Можливо, ваші потреби будуть задоволені, використовуючи:

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

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

Нарешті:

Чесно кажучи, я просто скористався 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.

Все інше те саме. Покладіть, ймовірно , не буде блокуватись, оскільки ви, ймовірно, не поставите два мільярди об’єктів у чергу.


Дякую за Вашу відповідь. Ще одне питання: чи потрібно мені це робити методом видалення: while (queue.size () == 0) wait (); queue.poll ();
Рікардо Фелгейраш

Я відповім на це як редагування своєї відповіді, оскільки це досить важливо.
Адам Яскевич

Як викликало замішання в іншому питанні, Collection.synchronizedListповертає Listякий не реалізує Queue.
Том Хотін - тайклін

@AdamJaskiewicz використовує ConcurrentLinkedQueueдля Producer споживача хороша ідея, я маю в виду це повідомлення stackoverflow.com/questions/1426754 / ...
rd22

37

Це багато в чому дублікат іншого питання .

Ось розділ цієї відповіді, що стосується цього питання:

Чи потрібно робити власну синхронізацію, якщо я використовую java.util.ConcurrentLinkedQueue?

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

Наприклад, це безпечна нитка без будь-яких дій з вашого боку:

queue.add(obj);

або

queue.poll(obj);

Однак; неатомарні дзвінки до черги не є автоматично безпечними для потоків. Наприклад, такі операції не є автоматично безпечними для потоків:

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

Останнє не є безпечним для потоків, оскільки дуже можливо, що між тим часом, коли викликається порожній і викликається опитуванням часу, інші потоки додаватимуть чи видалятимуть елементи з черги. Надійний спосіб виконати це так:

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

Знову ж таки ... атомарні виклики до черги автоматично безпечні для потоку. Неатомні дзвінки ні.


1
Єдине поширене використання, яке я можу заблокувати, це блокування, поки черга не буде порожньою. Хіба це не було б краще обслуговувати, використовуючи реалізацію BlockingQueue (take () є атомним і блокує, поки є що споживати)?
Адам Яскевич 05.03.09

6
Також з цим потрібно бути обережним. На відміну від синхронізованого списку, ConcurrentLinkedQueue не синхронізується сам по собі, тому у вашому коді виробник все ще може запропонувати в чергу, поки ви знаходитесь у своєму синхронізованому блоці.
Адам Яскевич 05.03.09

Чому б ви поєднали queue.isEmpty () та queue.poll ()? Ви не можете просто опитати і перевірити, чи є результат нульовим? (я розумію, що якщо ви
опитуєте

Я погоджуюся з усім у вашому коді, крім останнього блоку коду. Синхронізація в ConcurrentLInkedQueue не гарантує вам нічого, оскільки інші дзвінки не синхронізуються. Отже, може статися «add (..)» з іншого потоку між вашими «isEmpty ()» та «poll (...)», навіть якщо ви синхронізували цей потік
Klitos G.,


6

Це, мабуть, саме те, що ви шукаєте з точки зору безпеки потоків та "гарності", намагаючись використати все в черзі:

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

Це гарантуватиме, що ви вийдете, коли черга порожня, і продовжуватимете виводити об’єкти з неї, доки вона не порожня.


Дуже корисно для спорожнення черги. Дякую.
DevilCode

2

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


1

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

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


ConcurrentLinkedQueue не є. Він спеціально побудований з нуля для одночасного доступу декількох виробників та споживачів. Трохи вигадливіший за прості обгортки, повернені колекціями.
Синхронізовано

Так, у J2SE 5 було додано багато акуратних одночасних речей
Адам Яскевич,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.