3. 3
Чего не будет в презентации
• Определений
из
учебника
- Больше
интересует
сама
концепция
• Сравнения
производительности
• Советов,
как
правильно
писать
код
• Холиваров*
• Серебряных
пуль,
волшебных
фреймворков и
т.п.
*
Нет,
ну
если
кто-‐то
очень
захочет,
то
можно,
конечно…
4. 4
А что будет-то?
• Пара
простых
многопоточных
примеров
• Куча
связанных
с
ними
проблем
• Варианты
решений
- Которые,
разумеется,
не
работают
- Ну
некоторые
работают
- Иногда
- Наверное…
5. 5
Пререквизиты
Нужно
примерно понимать,
что
такое
• Thread
• Runnable
• synchonized
• Lock lock = new ReentrantLock(false)
• volatile
• AtomicInteger
• Compare-And-Set
6. 6
Стенд
• Apple
MacBook
Pro
Retina,
2014
- Intel
Core
i7
- 4
cores
x
2
threads
=
8
HW
threads
- 2,3
GHz
- 16
Gb
RAM
- Oracle
JDK
8
update
60
• Mac
OS
X
10.10.5
8. 8
• 5
философов
по
кругу
– Тарелка
с
едой
перед
каждым
– Вилки
между
тарелками
• Каждый
может
– Размышлять
– Брать
соседнюю
вилку
– Есть
(строго
двумя
вилками!)
– Класть
одну
вилку
Задача о философах
9. 9
Проблемы с обедающими философами
Пусть
каждый
философ
действует
по
некоторому
алгоритму
• Могут
ли
все
философы
умереть
с
голоду?
• Можно
ли
составить
такой
алгоритм,
чтобы
все
философы
гарантированно не
умерли
с
голоду?
10. 10
Параметры задачи
• Количество
философов
• Есть
ли
возможность
положить
вилку,
не
пожрамши
• Сколько
времени
философ
ест
- Фиксированное
или
случайная
величина
• Сколько
времени
философ
размышляет
- Фиксированное
или
случайная
величина
- Как
это
время
соотносится
с
временем
еды
• Какие
ещё
инструменты/элементы
есть
в
системе?
• Что
ещё?
11. 11
Фиксированное время и случайное время
• Случайное
время
задаётся
какой-‐то
функцией
распределения
• Константа
тоже задаётся
функцией
распределения
- (А
бывают
ли
вообще
константы
в
реальном
мире?)
12. 12
Фиксированное время и случайное время
• Случайное
время
задаётся
какой-‐то
функцией
распределения
• Константа
тоже задаётся
функцией
распределения
- (А
бывают
ли
вообще
константы
в
реальном
мире?)
16. 16
Простое решение
• Когда
философ
хочет
есть,
он
делает
следующие
шаги:
1. Берёт
левую
от
себя
вилку
(«сено»)
2. Берёт
правую
от
себя
вилку
(«солома»)
3. Ест
4. Кладёт
одну
вилку
5. Кладёт
другую
вилку
6. Размышляет
• И
так
по
кругу
22. 22
Взаимоблокировка (Deadlock)
Взаимоблокировка – такое
состояние
системы,
при
котором
два
или
более
процессов
не
могут
продолжать
своё
выполнение
из-‐за
отсутствия
необходимых
для
этого
ресурсов.
Каждый
ждёт
другого,
поэтому
никто
не
может
продолжить
23. 23
Выгружаемые и невыгружаемые ресурсы
• Выгружаемые
ресурсы
— ресурсы,
которые
могут быть
безболезненно
отобраны
у
процесса,
который
ими
обладает
• Невыгружаемые
ресурсы
— ресурсы,
которые
нельзя
отобрать
у
процесса,
не
вызвав
при
этом
сбой
в
вычислениях
• Мы
будем
говорить,
в
основном,
о
невыгружаемых
ресурсах
24. 24
Операции над невыгружаемыми ресурсами
• Запрос
ресурса
- Берём ресурс
- или
ждём (встаём
в
«очередь»
ожидания)
• Использование
ресурса
• Освобождение
ресурса
28. 28
Условие удержания и ожидания
Процессы,
удерживающие
в
данный
момент
ранее
выделенные
им
ресурсы,
могут запрашивать
новые
ресурсы.
29. 29
Условие невыгружаемости
Ранее
выделенные
ресурсы
не
могут
быть
принудительно
отобраны у
процесса.
Они
должны
быть
явным
образом
высвобождены
тем
процессом,
который
их
удерживает.
30. 30
Условие циклического ожидания
Должна
существовать
кольцевая
последовательность
из
двух
и
более
процессов,
каждый
из
которых
ожидает
высвобождения
ресурса,
удерживаемого
следующим
членом
последовательности.
37. 37
Стратегии борьбы с блокировками
• Игнорирование
проблемы
• Обнаружение
и
восстановление
• Динамическое
уклонение
• Предотвращение
за
счёт
подавления
любого
из
четырёх
условий
Коффмана
40. 40
Кто использует стратегии борьбы?
• Базы
данных
- Блокировки
на
строках,
таблицах,
индексах
и
т.д.
• JVM
- А
вот
и
нет!
- Ручками,
ручками!
- Ну
и
головой…
41. 41
Алгоритм Страуса
(Делаем
вид,
что
проблема
отсутствует)
• Насколько
часто
возникает
проблема?
• Как
часто
возникают
сбои
в
системе
по
другим
причинам?
• Насколько
серьёзны
могут
быть
последствия?
42. 42
Обнаружение взаимоблокировок и
восстановление работоспособности
• Шаги
- Позволить
блокировке
произойти
- Пытаться
обнаружить
момент
возникновения
- Попробовать
восстановить
работоспособность
В
нашем
примере
можно
просто
перезапускать
философов
43. 43
Выход из взаимоблокировки
• Приоритетный
захват
ресурсов
- Приоритезировать (все)
процессы
- Отобрать
ресурс
у
менее
приоритетного
процесса
• Откат
(см.
след.
слайд)
• Уничтожение
и
перезапуск
процессов
44. 44
Выход из взаимоблокировки — Откат
• Периодически
создаются
контрольные
точки
• При
обнаружении
блокировки
происходит
откат
- При
откате
часть
работы
(которая
была
выполнена
после
прохождения
последней
контрольной
точки)
теряется
48. 48
Предотвращение взаимоблокировки
• Атака
условия
взаимного
исключения
• Атака
условия
ожидания
и
удержания
• Атака
условия
циклического
ожидания
• Атака
условия
невыгружаемости
49. 49
Атака условия взаимного исключения
• Возможна
редко
— часто
программа
становится
некорректной
• Идея
— убирать
ненужные
блокировки
- Делать
нужно
осторожно,
чтобы
функциональность
не
страдала
- Заменять
на
другие
механизмы
50. 50
Атака условия ожидания и удержания
• Запрашивать
ВСЕ
необходимые
ресурсы
не
в
процессе
работы,
а
до
начала
работы.
- Но
не
всегда
ресурсы
известны
заранее
• Вначале
временно
высвободить
все
удерживаемые
ресурсы
54. 54
Голодание (Starvation)
• Голодание — ситуация,
в
которой
поток,
от
которого
ожидается
прогресс,
(практически)
стоит
на
месте.
55. 55
Голодание (Starvation)
• Голодание — ситуация,
в
которой
поток,
от
которого
ожидается
прогресс,
(практически)
стоит
на
месте.
• Заблуждение
- Голодание
может
осуществиться,
только
если
потоки
с
более
высоким
приоритетом
постоянно
берут
ресурсы,
которые
нужны
голодающему
потоку
60. 60
Что же делать
• Решение
с
посредником: официант
решает,
кому
можно
брать
вилку,
а
кому
нет
- Решение
с
общим
критическим
ресурсом
- Решение
с
семафором
68. 68
Модели
• Модель
с
разделяемой
памятью
- Регистры
- Операции:
read,
write
- Удобно
программировать,
все
привыкли
• Модель
с
передачей
сообщений
- Послать
сообщение
- Похожа
на
то,
как
реально
работает
железо
71. 71
Параллелизм — ОС
- Слушать
музыку
и
переписываться
в
фейсбуке в
Одноклассниках
- При
зависании
одной
программы
другие
продолжают
работать
- и
т.п.
72. 72
Преимущества параллелизма
• Использование
нескольких
ядер/процессоров
- Да
и
на
1
ядре
тоже!
(async I/O)
• Простота
моделирования
- Абстракция:
фреймворк забирает
сложность
• Упрощенная
обработка
асинхронных
событий
• Более
отзывчивые
интерфейсы
пользователя
- Event
Dispatch
Thread
(EDT),
async calls
73. 73
Параллелизм на уровне отдельно взятой программы
• Эффективное
использование
ресурсов
• Удобство,
простота
написания
кода
• Справедливость
- Обработка
запросов
пользователей
на
серверах
соцсети с
одинаковым
приоритетом
- Читатели
и
писатели
- Fairness
(честность)
79. 79
Проблемы блокировок
• Взаимоблокировки
(Deadlocks)
• Инверсия
приоритетов
• Надежность
— вдруг
владелец
блокировки
помрет?
• Performance
- Параллелизма
в
критической
секции
нет!
- Владелец
блокировки
может
быть
вытеснен
планировщиком
80. 80
Закон Амдала
• α — часть общего объема вычислений,
которую нельзя распараллелить
• 1-α — часть, которую можно
распараллелить
• p — количество потоков
81. 81
Закон Амдала
• α — часть общего объема вычислений,
которую нельзя распараллелить
• 1-α — часть, которую можно
распараллелить
• p — количество потоков
83. 83
Классификация
• Без
препятствий
(Obstruction-‐Free)
— поток
совершает
прогресс,
если
не
встречает
препятствий
со
стороны
других
потоков
• Без
блокировок
(Lock-‐Free) — гарантируется
системный
прогресс
хотя
бы
одного
потока
• Без
ожидания (Wait-‐Free) — каждая
операция
выполняется
за
фиксированное
число
шагов,
не
зависящее
от
других
потоков
84. 84
Консенсус
• Объект
consensus
с
операцией
decide(v):
- consensus.decide(v)
≠
const
- wait-‐free
• N
Потоков
вызывают
consensus.decide()
- i-‐ый поток
вызывает
consensus.decide(vi)
- Каждый
поток
вызывает
не
более
1
раза
- decide() возвращает
одно
из
vi
• decide()
— протокол
консенсуса
85. 85
Консенсусное число
• Мощность
консенсуса
— максимальное
количество
(N) потоков,
для
которых
данный
объект
обеспечивает
консенсус
• Консенсусное число
примитива
синхронизации
—
максимальная
мощность
консенсуса,
который
можно
построить
на
базе
данного
примитива и
некоторого
количества
атомарных
регистров
- То
есть,
существует
реализация
метода
decide
для
N
потоков,
использующая
данный
примитив
как
строительный
блок
86. 86
Консенсусные числа различных операций
• Операции
на
регистрах
— 1
• Read-‐Modification-‐Write
(RMW) — 2
- Common2
Class — коммутируют
друг
с
другом
или
перезаписывают
друг
друга
- Универсальные
операции
— ∞
- Сравнение
с
обменом
(CAS):
Compare-‐And-‐Swap,
Compare-‐And-‐Set
89. 89
CAS Loop — типичный паттерн применения
1. Прочитать
значение
A
из
переменной
V
2. Взять
какое-‐то
новое
значение
B
для
V
3. Использовать
CAS
для
атомарного
изменения
V
из
A
в
B до
тех
пор,
пока
другие
потоки
меняют
значение
V
во
время
этого
процесса
Атомарность Read-‐Modify-‐Write
реализуется
за
счет
постоянного
мониторинга системы
на
предмет
постороннего
вмешательства
91. 91
Fast vs. slow path
• Каждый
блок
кода
может
иметь,
как
минимум,
два
пути
исполнения:
короткий
и
длинный
• Lock:
contended
vs.
Uncontended
• Uncontended
Lock:
- ≥
1
CAS
92. 92
Недостатки CAS
• CAS
заставляет
потоки,
которые
его
вызывают,
работать
в
условиях
соревнования
(contention)
- Больше
contention
=
больше
бесполезных
циклов
процессора,
трата
процессорного
времени
• Написание
корректных
и
быстрых
алгоритмов
на
CAS
требует
специальной
подготовки
94. 94
Поддержка CAS в Java
• В
Java
5
появился
JSR166
- пакет
java.util.concurrent
- пакет
java.util.concurrent.atomic
• На
платформах,
поддерживающих
CAS,
JIT-‐
компилятор
делает
inline
соответствующих
машинных
инструкций
• Load
Linked
/
Store
Conditional
98. 98
AtomicInteger
• boolean compareAndSet(int expect, int update)
• int addAndGet(int delta)
• int getAndDecrement()
• int getAndIncrement()
• int incrementAndGet()
• …
Эти
операции
— блокирующие?
101. 101
Field Updaters
• AtomicIntegerFieldUpdater
- Reflection-‐based
updater
for
volatile
int
• AtomicLongFieldUpdater
- Reflection-‐based
updater
for
volatile
long
• AtomicReferenceFieldUpdater
- Reflection-‐based
updater
for
volatile
object
102. 102
AtomicLongFieldUpdater
- long
addAndGet(T
obj,
long
delta)
- boolean compareAndSet(T
obj,
long
expect,
long
update)
- long
getAndAdd(T
obj,
long
delta)
- long
incrementAndGet(T
obj)
106. 106
AtomicLongArray
• long addAndGet(int i, long delta)
• long getAndAdd(int i, long delta)
• boolean compareAndSet(int i, long
expect, long update)
• long incrementAndGet(int i)
• …
107. 107
Compound Variables
• AtomicMarkableReference
- compareAndSet(
V expectedReference, V newReference,
boolean expectedMark, boolean newMark)
• AtomicStampedReference
- boolean compareAndSet(
V expectedReference, V newReference,
int expectedStamp, int newStamp)
110. 110
• Алгоритм
называется
неблокирующим
(nonblocking),
если
отказ
или
остановка
любого
потока
не
может
привести
к
отказу
или
остановке
любого
другого
потока
• Алгоритм
называется
свободным
от
блокировок(lock-‐free),
если
на
каждом
шаге
какой-‐то
поток
выполняет
работу
(make
progress)
• nonblocking и
lock-‐free
— это
разные
вещи!
- Алгоритмы
на
CAS
могут быть
одновременно
неблокирующими
и
свободными
от
блокировок
110
Неблокирующие алгоритмы