2010-04-18 2 views
64

Я читаю книгу Java Concurrency in Practice. В главе 15 речь идет о неблокирующих алгоритмах и методе сравнения и замены (CAS).Java Concurrency: CAS vs Locking

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

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

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

ответ

37

CAS обычно намного быстрее, чем блокировка, но это зависит от степени раздора. Поскольку CAS может принудительно повторить попытку, если значение изменится между чтением и сравнением, поток теоретически может застрять в оживленном ожидании, если рассматриваемая переменная сильно ударит по многим другим потокам (или если дорого вычислить новое значение от старого значения (или обоих)).

Основная проблема с CAS заключается в том, что программировать гораздо сложнее, чем при блокировке. Имейте в виду, что блокировка, в свою очередь, намного сложнее использовать правильно, чем передача сообщений, или STM, поэтому не принимайте это как подтверждение звонка на использование блокировок.

+0

Согласен, акцентируя внимание на * счете вычисления нового значения *. Часто он настолько велик, что стратегия блокировки не блокируется на основе блокировки. –

16

Вы можете посмотреть числа между ConcurrentLinkedQueue и BlockingQueue. Что вы увидите, так это то, что CAS заметно быстрее при умеренных (более реалистичных в реальных приложениях) потоках.

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

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

Вы должны прочитать о Michael and Scott Queue, это реализация очереди для ConcurrentLinkedQueue и объясняет, как обрабатывать двухстороннюю, потокобезопасную, атомную функцию с одним CAS.

+1

Интересна статья о «Майкл и Скотт Очередь». Спасибо! – Prine

12

Там хорошая книга тесно связана с темой безблокировочного параллелизм: «Искусство программирования многопроцессорных» Мориса Herlihy

+0

Также его презентация [часть 1] (http://research.microsoft.com/apps/video/default.aspx?id=168298) и [часть 2] (http://research.microsoft.com/apps/video /default.aspx?id=169831) очень полезны. –

23

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

Неблокирующие алгоритмы обычно лучше масштабируются, потому что они имеют более короткие «критические разделы», чем алгоритмы на основе блокировок.

5

Если вы ищете сравнение в реальном мире, вот один. Наше приложение имеет два (2) потока: 1) поток считывателя для захвата сетевого пакета и 2) потребительский поток, который принимает пакет, подсчитывает его и сообщает статистику.

Thread # 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 - когда мы заменяем нашу реализацию КАС стандартной Java S ynchronousQueue:

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 

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