Паралельність Java: CAS проти блокування [закрито]


76

Я читаю книгу " Паралельно Java" на практиці . У главі 15 вони говорять про алгоритми блокування та метод порівняння та обміну (CAS).

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

Для мене використання замків набагато зрозуміліше та легше зрозуміти та, можливо, навіть краще підтримувати (будь ласка, виправте мене, якщо я помиляюся) . Чи справді нам слід зосередитись на створенні нашого одночасного коду, пов'язаного з CAS, ніж блокування, щоб отримати кращий приріст продуктивності, чи стабільність важливіша?

Я знаю, що, можливо, не існує суворого правила, коли що використовувати. Але я просто хотів би почути деякі думки та досвід щодо нової концепції CAS.


Звичайне блокування за допомогою об'єктів монітора включає CAS, а також блокування викликів ядра. Cass полегшує блокування, так що не буде виклику ядра без суперечок. І коли виникають суперечки, CAS може деякий час обертатися, а потім відбуватиметься виклик ядра або негайно відбуватиметься виклик ядра.
Боніта Монтеро,

Відповіді:


48

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

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


1
Я згоден, роблячи акцент за рахунок обчислення нового значення . Часто він настільки великий, що змушує програму, що не блокується, програвати на основі блокування.
Alex Salauyou,

28

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

Неблокувальні алгоритми зазвичай масштабуються краще, оскільки вони мають більш короткі "критичні секції", ніж алгоритми на основі блокування.


23

Ви можете подивитися цифри між a ConcurrentLinkedQueueта a BlockingQueue. Що ви побачите, це те, що CAS помітно швидший за помірних (більш реалістичних у реальних додатках) суперечок потоків.

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

Щоб відповісти на ваші запитання, так, неблокуючі потокові алгоритми або колекції ( ConcurrentLinkedQueue,ConcurrentSkipListMap/Set ) можуть бути значно швидшими, ніж їх аналоги, що блокують. Як зазначив Марсело, отримати правильні неблокуючі алгоритми дуже складно і вимагає великої уваги.

Вам слід прочитати про чергу Майкла та Скотта , це реалізація черги для ConcurrentLinkedQueueі пояснює, як обробляти двосторонню, безпечну для потоку атомну функцію з одним CAS .


1
Стаття про "чергу Майкла і Скотта" цікава. Дякую!
Prine


9

Якщо ви шукаєте реальне порівняння, ось одне. У нашому додатку є два (2) потоки 1) потік зчитувача для захоплення мережевих пакетів та 2) потік споживача, який приймає пакет, підраховує його та передає статистику.

Потік №1 обмінює по одному пакету з потоком №2

Результат №1 - використовує власний обмін на основі CAS, використовуючи ті самі принципи, що і SynchronousQueue , де наш клас називається CASSynchronousQueue :

30,766,538 packets in 59.999 seconds ::  500.763Kpps, 1.115Gbps 0 drops
libpcap statistics: recv=61,251,128, drop=0(0.0%), ifdrop=0

Результат №2 - коли ми замінюємо нашу реалізацію CAS на стандартний java SynchronousQueue :

8,782,647 packets in 59.999 seconds ::  142.950Kpps, 324.957Mbps 0 drops 
libpcap statistics: recv=69,955,666, drop=52,369,516(74.9%), ifdrop=0

Я не думаю, що різниця в продуктивності не може бути більш чіткою.

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