Параллелизм Java: CAS против блокировки [закрыто]
Я читаю книгу параллелизм Java на практике. В главе 15 они говорят о неблокирующих алгоритмах и compare-and-swap (CAS) метод.
написано, что CAS работают намного лучше, чем методы блокировки. Я хочу спросить людей, которые уже работали с обеими этими концепциями и хотели бы услышать, когда вы предпочитаете какой из этих концепций? Неужели это так много быстрее?
для меня использование замков гораздо яснее и проще понять и, возможно, даже лучше поддерживать (пожалуйста, поправьте меня, если я ошибаюсь). Должны ли мы действительно сосредоточиться на создании нашего параллельного кода, связанного с CAS, чем блокировки, чтобы получить лучший прирост производительности или устойчивость более важна?
Я знаю, что, возможно, нет строгого правила, когда использовать что. но я просто хотел бы услышать некоторые мнения, опыт работы с новой концепцией КАС.
5 ответов:
CAS обычно намного быстрее, чем блокировка, но это зависит от степени конкуренции. Поскольку CAS может принудительно повторить попытку, если значение изменяется между чтением и сравнением, поток теоретически может застрять в занятом ожидании, если рассматриваемая переменная сильно пострадала от многих других потоков (или если дорого вычислить новое значение из старого значения (или оба)).
основная проблема с CAS заключается в том, что гораздо сложнее правильно программировать, чем блокировать. Имейте в виду, блокировка, в свою очередь, гораздо сложнее использовать правильно, чем передача сообщений или СТМ, так что не принимайте это как сигнал одобрения для использования замков.
относительная скорость операций в значительной степени не проблема. Важным является различие в масштабируемости между алгоритмами блокировки и неблокирующими алгоритмами. И если вы работаете на 1 или 2 ядра системы, перестать думать о таких вещах.
неблокирующие алгоритмы обычно масштабируются лучше, потому что они имеют более короткие "критические секции", чем алгоритмы на основе блокировки.
вы можете посмотреть на цифры между a
ConcurrentLinkedQueue
иBlockingQueue
. То, что вы увидите это CAS заметно быстрее при умеренной (более реалистичной в реальных приложениях) конкуренции потоков.самое привлекательное свойство неблокирующий алгоритмы-это тот факт, что если один поток терпит неудачу (Cache miss или хуже, seg fault), то другие потоки не заметят этого сбоя и могут двигаться дальше. Однако при приобретении замка, если замок удерживается поток имеет какой-то сбой ОС, каждый другой поток, ожидающий освобождения блокировки, также будет поражен сбоем.
чтобы ответить на ваши вопросы, да, неблокирующие потокобезопасные алгоритмы или коллекции (
ConcurrentLinkedQueue
,ConcurrentSkipListMap/Set
) может быть значительно быстрее, чем их блокирование аналоги. Однако, как отметил Марсело, получение правильных неблокирующих алгоритмов очень сложно и требует большого внимания.вы должны прочитать о Майкл и Скотт Очереди, это реализация очереди для
ConcurrentLinkedQueue
и объясняет, как обрабатывать двустороннюю, потокобезопасную, атомарную функцию с одним CAS.
есть хорошая книга, сильно связанная с темой параллелизма без блокировки: "искусство многопроцессорного программирования" Мориса Херлихи
Если вы ищете сравнение реального мира, вот один. Наше приложение имеет два (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
Я не думаю, что разница в производительности не может быть более понятнее.