Алексей	Федоров,	
Одноклассники
Синхронизация без блокировок и СМС
Зачем вы здесь?
3
4
Concurrency
5
Concurrency
6
Много интересных докладов в других залах
7
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
8
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
Новичкам в неблокирующей
синхронизации
Короткое введение
9
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
Новичкам в неблокирующей
синхронизации
Короткое введение
Продвинутым многопоточным
программистам
Поговорим о деталях
реализации CAS/atomics!
10
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
Новичкам в неблокирующей
синхронизации
Короткое введение
Продвинутым многопоточным
программистам
Поговорим о деталях
реализации CAS/atomics!
Хипстерам Поговорим про хайп!
Immutable vs. Mutable
11
Модели
• Модель	с	разделяемой	памятью
- Операции	на	атомарных	регистрах:	read,	write
- Удобно	программировать,	все	привыкли
• Модель	с	передачей	сообщений
- Операции:	send,	onReceive
- Похожа	на	то,	как	реально	работает	железо
12
Терминология
• Нет	устоявшейся	терминологии
• Термины:
- Parallel
- Concurrent
- Distributed
13
Виды параллелизма
• На	уровне	операционной	системы
• На	уровне	одной	программы	/	процесса
14
Параллелизм — ОС
• Слушать	музыку	и	переписываться	в	фейсбуке
в	Одноклассниках
• При	зависании	одной	программы	другие	
продолжают	работать
• и	т.п.
15
Преимущества параллелизма
• Использование	нескольких ядер/процессоров
- Да	и	на	1	ядре	тоже!	(async I/O)
• Простота моделирования
- Абстракция:	фреймворк забирает	сложность
• Упрощенная	обработка	асинхронных событий
• Более	отзывчивые интерфейсы	пользователя
- Event	Dispatch	Thread	(EDT),	async calls
16
Параллелизм на уровне отдельно взятой программы
• Эффективное использование	ресурсов
• Удобство,	простота написания	кода
• Справедливость	
- Обработка	запросов	пользователей	на	серверах	
соцсети с	одинаковым	приоритетом
- Читатели	и	писатели
- Fairness	(честность)
17
Lock lock = new ReentrantLock(true);
18
Честность!
Lock lock = new ReentrantLock(true);
19 Блокировки
Old School
wait()
notify()
notifyAll()
synchronized {
doSomething();
}
public synchronized foo() {
doSomethingElse();
}
В	языке
20 Блокировки
Old School
wait()
notify()
notifyAll()
synchronized {
doSomething();
}
public synchronized foo() {
doSomethingElse();
}
Lock lock = new ReentrantLock();
try {
lock.lock();
doSomething();
} finally {
lock.unlock();
}
В	языке В	библиотеке	(JDK)
java.util.concurrent.*
java.util.concurrent.atomic.*
21
Counter
public interface Counter {
long get();
void increment();
}
22
Simple Counter
public class SimpleCounter implements Counter {
long value = 0;
public long get() {
return value;
}
public void increment() {
value++;
}
}
23
Volatile Counter
public class VolatileCounter implements Counter {
volatile long value = 0;
public long get() {
return value;
}
public void increment() {
value++;
}
}
24
Synchronized Counter
public class SynchronizedCounter implements Counter {
long value = 0;
public synchronized long get() {
return value;
}
public synchronized void increment() {
value++;
}
}
25
Проблемы блокировок
• Взаимоблокировки (Deadlocks)
26
Проблемы блокировок
• Взаимоблокировки (Deadlocks)
• Инверсия приоритетов
27
Проблемы блокировок
• Взаимоблокировки (Deadlocks)
• Инверсия приоритетов
• Надежность — вдруг владелец блокировки помрет?
28
Проблемы блокировок
• Взаимоблокировки (Deadlocks)
• Инверсия приоритетов
• Надежность — вдруг владелец блокировки помрет?
• Performance
– Параллелизма в критической секции нет!
– Владелец блокировки может быть вытеснен планировщиком
29
Закон	Амдала
α часть общего объема вычислений,
которую нельзя распараллелить
1-α часть, которую можно распараллелить
p количество потоков
30
Закон	Амдала
Sp=
𝟏
α#	
𝟏%α
𝐩
α часть общего объема вычислений,
которую нельзя распараллелить
1-α часть, которую можно распараллелить
p количество потоков
Нам нужно что-то другое
Добро пожаловать
в неблокирующую синхронизацию!
33
If-Modify-Write
volatile int value = 0;
if (value == 0) {
value = 42;
}
No	Atomicity
in	terms	of	JMM
34
Compare and Swap
int value = 0;
synchronized (...) {
if (value == 0) {
value = 42;
}
}
35
Введем волшебную операцию
int value = 0;
i.compareAndSet(0, 42);
36
Compare	and	Swap	— Hardware	Support
compare-and-swap
CAS
load-link / store-conditional
LL/SC
cmpxchg
ldrex/strex lwarx/stwcx
37
CAS Semantics
public class PseudoCAS {
private long value;
public synchronized long get() { return value; }
public synchronized long compareAndSwap(long expected, long newV) {
long oldValue = value;
if (oldValue == expected) {
value = newV;
}
return oldValue;
}
public synchronized boolean compareAndSet(long expected, long newV){
return expected == compareAndSwap(expected, newV);
}
}
38 Pseudo-CAS Counter
public class PseudoCasLoopCounter implements Counter {
private PseudoCAS value = new PseudoCAS();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
39 Pseudo-CAS Counter
public class PseudoCasLoopCounter implements Counter {
private PseudoCAS value = new PseudoCAS();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
CAS in Java
41
CAS in Java
Since Java 5, JSR166
java.util.concurrent.atomic
• Scalars
• Field updaters
• Arrays
• Compound variables
• Accumulators/Adders
since Java 8
42
Scalars
• AtomicBoolean
• AtomicInteger
• AtomicLong
• AtomicReference
43
Scalars
• AtomicBoolean
• AtomicInteger
• AtomicLong
• AtomicReference
44
• boolean compareAndSet(long expect, long update)
• long addAndGet(long delta)
• long getAndAdd(long delta)
• long getAndDecrement()
• long getAndIncrement()
• long incrementAndGet()
• …
AtomicLong
45
• boolean compareAndSet(long expect, long update)
• long addAndGet(long delta)
• long getAndAdd(long delta)
• long getAndDecrement()
• long getAndIncrement()
• long incrementAndGet()
• …
AtomicLong
46 CAS Counter
public class CasLoopCounter implements Counter {
private AtomicLong value = new AtomicLong();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
47
Недостатки CAS
Contended CAS —> tons of useless CPU cycles
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
Написание быстрых и корректных алгоритмов на CAS
требует экспертизы
48
AtomicLong
• boolean compareAndSet(long expect, long update)
• long addAndGet(long delta)
• long getAndAdd(long delta)
• long getAndDecrement()
• long getAndIncrement()
• long incrementAndGet()
• …
49
Get-and-Add Counter
public class CasLoopCounter implements Counter {
private AtomicLong value = new AtomicLong();
public long get() {
return value.get();
}
public void increment() {
value.getAndAdd(1);
}
}
50
atomicLong.getAndAdd(5)
JDK	7u95	 JDK	8u72	
60
9 7 6
100
27 27 27
1 2 3 4
ops	/	μs
threads
51
atomicLong.getAndAdd(5)
JDK	7u95	 JDK	8u72	
60
9 7 6
100
27 27 27
1 2 3 4
ops	/	μs
threads
52
atomicLong.getAndAdd(5)
JDK	7u95	 JDK	8u72	
60
9 7 6
100
27 27 27
1 2 3 4
ops	/	μs
threads
53
loop:
mov 0x10(%rbx),%rax
mov %rax,%r11
add $0x5,%r11
lock cmpxchg %r11,0x10(%rbx)
sete %r11b
movzbl %r11b,%r11d
test %r10d,%r10d
je loop
JDK	7u95				-XX:+PrintAssembly
atomicLong.getAndAdd(5)
54
lock addq $0x5,0x10(%rbp))loop:
mov 0x10(%rbx),%rax
mov %rax,%r11
add $0x5,%r11
lock cmpxchg %r11,0x10(%rbx)
sete %r11b
movzbl %r11b,%r11d
test %r10d,%r10d
je loop
JDK	7u95				-XX:+PrintAssembly JDK	8u77				-XX:+PrintAssembly
atomicLong.getAndAdd(5)
JDK	7u95	 JDK	8u72	
60
9 7 6
100
27 27 27
1 2 3 4
ops	/	μs
threads
55 AtomicLong.getAndAdd()	— JDK	7
56 AtomicLong.getAndAdd()	— JDK	7
cmpxchg
57 AtomicLong.getAndAdd()	— JDK	8
58 AtomicLong.getAndAdd()	— JDK	8
lock addq
JVM	
Intrinsic
59
counterType Cnt Score Error Units
LONG 5 211.247 ± 7.532 ops/us
VOLATILE_LONG 5 24.837 ± 1.664 ops/us
ALFU 5 27.272 ± 1.946 ops/us
SYNCHRONIZED 5 10.231 ± 0.592 ops/us
UNFAIR_LOCK 5 19.959 ± 4.268 ops/us
FAIR_LOCK 5 0.225 ± 0.009 ops/us
CAS_LOOP 5 6.125 ± 0.225 ops/us
ADD_AND_GET 5 26.701 ± 0.620 ops/us
Бенчмарки!
Thanks to Nitsan Wakart
http://psy-lob-saw.blogspot.ru/2014/06/jdk8-update-on-scalable-counters.html
Core(TM) i7-4770
4 x 2 x 1.9 Ghz
(downgraded)
Ubuntu 14.04.3
3.13.0-83-generic
x86_64
taskset -c 0,1,2,3
(thread affinity)
60
Multivariable Invariant
61
Multivariable Invariant
62
Field Updaters
• AtomicIntegerFieldUpdater
– Reflection-based updater for volatile int
• AtomicLongFieldUpdater
– Reflection-based updater for volatile long
• AtomicReferenceFieldUpdater
– Reflection-based updater for volatile object
63
AtomicLongFieldUpdater
long addAndGet(T obj, long delta)
boolean compareAndSet(T obj, long exp, long upd)
long getAndAdd(T obj, long delta)
long incrementAndGet(T obj)
64
Volatile	Counter
public class VolatileCounter implements Counter {
volatile long value = 0;
public long get() {
return value;
}
public void increment() {
value++;
}
}
65
Volatile	Counter
public class VolatileCounter implements Counter {
volatile long value = 0;
public long get() {
return value;
}
public void increment() {
value++;
}
}
66 AtomicLongFieldUpdater-based Counter
private final VolatileCounter counter = new VolatileCounter();
ALFU<VolatileCounter> updater
= ALFU.newUpdater(VolatileCounter.class, "value");
public AFUCounter() throws NoSuchFieldException {
Field field = VolatileCounter.class.getDeclaredField("value");
field.setAccessible(true);
}
public long get() {
return updater.get(counter);
}
public void increment() {
updater.addAndGet(counter, 1);
}
67
AtomicLongFieldUpdater
68
AtomicLongFieldUpdater
69
AtomicArrays
AtomicIntegerArray
AtomicLongArray
AtomicReferenceArray
long addAndGet(int i, long delta)
long getAndAdd(int i, long delta)
boolean compareAndSet(
int i, long exp, long upd)
long incrementAndGet(int i)
…
70
Compound Variables
AtomicMarkableReference V сompareAndSet (
V expectedRef, V newRef,
boolean expectedMark, boolean newMark
)
AtomicStampedReference boolean compareAndSet (
V expectedRef, V newRef,
int expectedStamp, int newStamp
)
71
Accumulators
• DoubleAccumulator
• DoubleAdder
• LongAccumulator
• LongAdder
• (Striped64)
• void accumulate(long x)
• long get()
• long getThenReset()
• void reset()
72
Non-blocking Guarantees
Wait-Free
без ожидания
Per-thread progress is guaranteed
Lock-Free
без блокировок
Overall progress is guaranteed
Obstruction-Free
без препятствий
Per-thread progress is guaranteed
if thread has been isolated
(with all obstructing threads suspended)
73 AtomicLong-based Counter
public class CasCounter implements Counter {
private AtomicLong value = new AtomicLong();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
Any	guarantees?
A. Wait-Free
B. Lock-Free
C. Obstruction-Free
D. No	guarantees
74 AtomicLong-based Counter
public class CasCounter implements Counter {
private AtomicLong value = new AtomicLong();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
Any	guarantees?
A. Wait-Free
B. Lock-Free
C. Obstruction-Free
D. No	guarantees
75 AtomicLong-based Counter
public class CasCounter implements Counter {
private AtomicLong value = new AtomicLong();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
Any	guarantees	on	x64?
A. Wait-Free
B. Lock-Free
C. Obstruction-Free
D. No	guarantees
76 AtomicLong-based Counter
public class CasCounter implements Counter {
private AtomicLong value = new AtomicLong();
public long get() {
return value.get();
}
public void increment() {
long v;
do {
v = value.get();
} while (value.compareAndSet(v, v + 1));
}
}
Any	guarantees	on	x64?
A. Wait-Free
B. Lock-Free
C. Obstruction-Free
D. No	guarantees
CAS в .NET
78
Interlocked
79
Interlocked
Неблокирующие	структуры	данных
81
Неблокирующий	
стек
82
Michael and Scott, 1996
https://www.research.ibm.com/people/m/michael/podc-1996.pdf
Потоки помогают друг другу
Неблокирующая	очередь
Материалы
84
Литература
85
Материалы
• Все-все-все	— bit.ly/concurrency-interest
• Nitsan Wakart — psy-lob-saw.blogspot.com
• Алексей	Шипилёв — shipilev.net
• Андрей	Акиньшин	— aakinshin.net/ru/blog/
Вопросы	и	ответы

Алексей Федоров