SlideShare a Scribd company logo
Как	потоки	помогают	
друг	другу
Алексей Федоров, JUG.ru
Алексей Федоров
about.me/23derevo
Java-конференции JUG.ru Group
Санкт-Петербург
3-4 ноября 2017
Москва
6-7 апреля 2018
— Программирование
— Алгоритмы
— Многопоточность
Про что этот доклад
Много интересных докладов в других залах
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
Про что этот доклад
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
Новичкам в неблокирующей
синхронизации
Короткое введение
Про что этот доклад
Новичкам в области
многопоточности
Sorry
Перейдите в другой зал
Новичкам в неблокирующей
синхронизации
Короткое введение
Продвинутым многопоточным
программистам
Поговорим о неблокирующей
синхронизации
Про что этот доклад
Модели
Модели
Модель с разделяемой памятью
— Операции на атомарных регистрах: read, write
— Удобно программировать, все привыкли
Модель с разделяемой памятью
— Операции на атомарных регистрах: read, write
— Удобно программировать, все привыкли
Модель с передачей сообщений
— Операции: send, onReceive
— Похожа на то, как реально работает железо
Модели
Преимущества параллелизма
— Использование нескольких ядер/процессоров
— Да и на 1 ядре тоже! (async I/O)
— Использование нескольких ядер/процессоров
— Да и на 1 ядре тоже! (async I/O)
— Простота моделирования: фреймворк забирает
сложность
Преимущества параллелизма
— Использование нескольких ядер/процессоров
— Да и на 1 ядре тоже! (async I/O)
— Простота моделирования: фреймворк забирает
сложность
— Упрощенная обработка асинхронных событий
Преимущества параллелизма
— Использование нескольких ядер/процессоров
— Да и на 1 ядре тоже! (async I/O)
— Простота моделирования: фреймворк забирает
сложность
— Упрощенная обработка асинхронных событий
— Более отзывчивые интерфейсы пользователя
— Event Dispatch Thread (EDT), async calls
Преимущества параллелизма
Проблемы блокировок
— Взаимоблокировки (Deadlocks)
— Взаимоблокировки (Deadlocks)
— Инверсия приоритетов
Проблемы блокировок
— Взаимоблокировки (Deadlocks)
— Инверсия приоритетов
— Надежность
— вдруг владелец блокировки помрет?
Проблемы блокировок
— Взаимоблокировки (Deadlocks)
— Инверсия приоритетов
— Надежность
— вдруг владелец блокировки помрет?
— Performance
— Параллелизма в критической секции нет!
— Владелец блокировки может быть вытеснен
планировщиком
Проблемы блокировок
Закон Амдала
α часть общего объема вычислений,
которую нельзя распараллелить
1-α часть, которую можно распараллелить
p количество потоков
Sp=
𝟏
α#	
𝟏%α
𝐩
α часть общего объема вычислений,
которую нельзя распараллелить
1-α часть, которую можно распараллелить
p количество потоков
Закон Амдала
Фундаментальные законы природы
— XIX век — законы Ньютона
Фундаментальные законы природы
— XIX век — законы Ньютона
— XX век — закон Мура
— тактовые частоты
— число транзисторов
Фундаментальные законы природы
— XIX век — законы Ньютона
— XX век — закон Мура
— тактовые частоты
— число транзисторов
— XXI век — закон Амдала
Фундаментальные законы природы
— XIX век — законы Ньютона
— XX век — закон Мура
— тактовые частоты
— число транзисторов
— XXI век — закон Амдала
Нарушение закона влечет за собой дисциплинарную,
гражданско-правовую, административную или
уголовную ответственность
If-Modify-Write
volatile int value = 0;
Есть ли в этом коде проблемы в
многопоточном окружении?
if (value == 0) {
value = 42;
}
If-Modify-Write
volatile int value = 0;
Нет атомарности
if (value == 0) {
value = 42;
}
Compare and Swap
int value = 0;
LOCK
if (value == 0) {
value = 42;
}
UNLOCK
Введем волшебную операцию
value.compareAndSet(0, 42);
int value = 0;
Симуляция CAS через synchronized
long value;
synchronized long get() {
return value;
}
synchronized long compareAndSwap(long expected, long newValue) {
long oldValue = value;
if (oldValue == expected) {
value = newValue;
}
return oldValue;
}
synchronized boolean compareAndSet(long expected, long newValue){
return expected == compareAndSwap(expected, newValue);
}
Compare and Swap — Hardware Support
compare-and-swap
CAS
load-link / store-conditional
LL/SC
cmpxchg ldrex/strex lwarx/stwcx
Atomics
AtomicReference
• r.get()
• r.compareAndSet(v1, v2)
• ...
AtomicLong
• i.get()
• i.compareAndSet(42, 43)
• i.incrementAndGet(1)
• i.getAndAdd(5)
• ...
Пример. Многопоточный счетчик
AtomicLong value = new AtomicLong();
long get() {
return value.get();
}
void increment() {
long v;
do {
v = value.get();
} while (!value.compareAndSet(v, v + 1));
}
Пример. Многопоточный счетчик
AtomicLong value = new AtomicLong();
long get() {
return value.get();
}
void increment() {
long v;
do {
v = value.get();
} while (!value.compareAndSet(v, v + 1));
}
Недостатки CAS
Contended CAS —> тонны бесполезных CPU-циклов
do {
v = value.get();
} while (!value.compareAndSet(v, v + 1));
Написание быстрых и корректных алгоритмов на CAS
требует экспертизы
class Node<E> {
final E item;
Node<E> next;
Node(E item) {
this.item = item;
}
}
...
class Node<E> {
final E item;
Node<E> next;
Node(E item) {
this.item = item;
}
}
E3
E1
E2
E3
E1
E2
top
E3
E1
E2
top
item1
Thread 1
E3
E1
E2
top
item1
Thread 1
E3
E1
E2
top
item2item1
Thread 1 Thread 2
E3
E1
E2
top
item2item1
Thread 1 Thread 2
E3
E1
E2
top
item2item1
Thread 1 Thread 2
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
AtomicReference<Node<E>> top;
E3
E1
E2
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
AtomicReference<Node<E>> top;
E3
E1
E2
item
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
E3
E1
E2
item
AtomicReference<Node<E>> top;
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
E3
E1
E2
AtomicReference<Node<E>> top;
item
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
AtomicReference<Node<E>> top;
E3
E1
E2
item
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
AtomicReference<Node<E>> top;
E3
E1
E2
item
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
AtomicReference<Node<E>> top;
E3
E1
E2
item
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
AtomicReference<Node<E>> top;
E3
E1
E2
item
top
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
E3
E1
E2
AtomicReference<Node<E>> top;
top
item
void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
E3
E1
E2
AtomicReference<Node<E>> top;
top
item
E pop() {
Node<E> newHead;
Node<E> oldHead;
do {
oldHead = top.get();
if (oldHead == null) return null;
newHead = oldHead.next;
} while (!top.compareAndSet(oldHead, newHead));
return oldHead.item;
}
E3
E1
E2
top
Michael and Scott, 1996
https://www.research.ibm.com/people/m/michael/podc-1996.pdf
Потоки помогают друг другу
Неблокирующая	очередь
class LinkedQueue<E> {
static class Node<E> {
final E item;
final AtomicReference<Node<E>> next;
Node(E item, AtomicReference<Node<E>> next) {
this.item = item;
this.next = next;
}
}
final Node<E> dummy = new Node<>(null, null);
final AtomicReference<Node<E>> head = new AtomicReference<>(dummy);
final AtomicReference<Node<E>> tail = new AtomicReference<>(dummy);
}
class LinkedQueue<E> {
static class Node<E> {
final E item;
final AtomicReference<Node<E>> next;
Node(E item, AtomicReference<Node<E>> next) {
this.item = item;
this.next = next;
}
}
final Node<E> dummy = new Node<>(null, null);
final AtomicReference<Node<E>> head = new AtomicReference<>(dummy);
final AtomicReference<Node<E>> tail = new AtomicReference<>(dummy);
}
class LinkedQueue<E> {
private static class Node<E> {
final E item;
final AtomicReference<Node<E>> next;
Node(E item, AtomicReference<Node<E>> next) {
this.item = item;
this.next = next;
}
}
final Node<E> dummy = new Node<>(null, null);
final AtomicReference<Node<E>> head = new AtomicReference<>(dummy);
final AtomicReference<Node<E>> tail = new AtomicReference<>(dummy);
}
tailhead
dummy 1 2
tailhead
dummy 1 2 3
tailhead
dummy 1 2 3
tailhead
dummy 1 2 3
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get(); // tailNext ?= null
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) { // tailNext != null
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) { // tailNext != null
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode); // true
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode); // true
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode); // false
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode); // false
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode); // false
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else { // tailNext == null
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode); // false
return true;
}
}
}
}
}
tail
head
dummy 1 2 item
Что получилось
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
}
public boolean put(E item) {
Node<E> newNode = new Node<>(item, null);
while (true) {
Node<E> currentTail = tail.get();
Node<E> tailNext = currentTail.next.get();
if (currentTail == tail.get()) {
if (tailNext != null) {
tail.compareAndSet(currentTail, tailNext);
} else {
if (currentTail.next.compareAndSet(null, newNode)) {
tail.compareAndSet(currentTail, newNode);
return true;
}
}
}
}
} АД И ЧЕРТИ
(ЗДЕСЬ ДОЛЖНА БЫТЬ КАРТИНКА)
ArrayBlockingQueue
ArrayBlockingQueue
0 1 2 3 4 N-1
...
void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
Как put() сделан в ArrayBlockingQueue
void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
Как put() сделан в ArrayBlockingQueue
void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
Как put() сделан в ArrayBlockingQueue
void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
Как put() сделан в ArrayBlockingQueue
void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await();
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length)
putIndex = 0;
count++;
notEmpty.signal();
} finally {
lock.unlock();
}
}
Как put() сделан в ArrayBlockingQueue
Выводы
— Неблокирующие алгоритмы могут быть очень сложными
— корректность написанного может быть не очевидна
— такие решения очень сложно поддерживать
— самописные решения кишат багами
— На практике такие решения используют редко
— Если ваши ограничения позволяют использовать
блокировки, не заморачивайтесь с lock-free
Выводы
— Неблокирующие алгоритмы могут быть очень сложными
— корректность написанного может быть не очевидна
— такие решения очень сложно поддерживать
— самописные решения кишат багами
— На практике такие решения используют редко
— Если ваши ограничения позволяют использовать
блокировки, не заморачивайтесь с lock-free
Выводы
Модификации
Ladan-Mozes, Shavit, 2004, 2008
Идея:	избавиться	от	второго	CAS
Optimistic	Approach
http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf
Hoffman, Shalev, Shavit, 2007
Baskets	Queue
http://people.csail.mit.edu/shanir/publications/Baskets%20Queue.pdf
— Есть выигрыш в производительности по сравнению
с обычной неблокирующей очередью
— за это мы платим потерей FIFO-свойства
— в реальной жизни «честный» FIFO нужен не всегда
— если вам нужно больше перфоманса, им можно
пожертвовать.
Baskets Queue
Материалы
Литература
Материалы
• Nitsan Wakart — http://psy-lob-saw.blogspot.com/
• Алексей	Шипилёв — https://shipilev.net/
• Максим	Хижинский — https://habrahabr.ru/post/219201/
Вопросы и ответы

More Related Content

PDF
Atomics, CAS and Nonblocking algorithms
Alexey Fyodorov
 
PDF
Помоги ближнему, или Как потоки помогают друг другу
CEE-SEC(R)
 
PDF
Unsafe: to be or to be removed?
Alexey Fyodorov
 
PDF
Thread
Alexander Rusin
 
PPTX
Multithreading in java past and actual
Yevgen Levik
 
PPTX
C sharp deep dive
Sergey Teplyakov
 
PPTX
Java 8. Thread pools
Nakraynikov Oleg
 
PPTX
Java threads - part 3
Nakraynikov Oleg
 
Atomics, CAS and Nonblocking algorithms
Alexey Fyodorov
 
Помоги ближнему, или Как потоки помогают друг другу
CEE-SEC(R)
 
Unsafe: to be or to be removed?
Alexey Fyodorov
 
Multithreading in java past and actual
Yevgen Levik
 
C sharp deep dive
Sergey Teplyakov
 
Java 8. Thread pools
Nakraynikov Oleg
 
Java threads - part 3
Nakraynikov Oleg
 

What's hot (18)

PPTX
Multiprocessor Programming Intro (lecture 3)
Dmitry Tsitelov
 
PPTX
Java threads - part 2
Nakraynikov Oleg
 
PPTX
Java threads - part 1
Nakraynikov Oleg
 
PPTX
разработка серверов и серверных приложений лекция №3
Eugeniy Tyumentcev
 
PDF
JavaScript. Event Loop and Timers (in russian)
Mikhail Davydov
 
PPT
Mike ponomarenko java17-fork-v1.2
Alex Tumanoff
 
PDF
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Sergey Platonov
 
PDF
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Sergey Platonov
 
PDF
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Sergey Platonov
 
PDF
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Yauheni Akhotnikau
 
PDF
10 - Java. Многопоточность в Java: основы
Roman Brovko
 
PDF
Wild Async .NET world: AID Kit for boy-scouts
HYS Enterprise
 
PDF
Борис Сазонов, RAII потоки и CancellationToken в C++
Sergey Platonov
 
PPTX
разработка серверов и серверных приложений лекция №2
Eugeniy Tyumentcev
 
PDF
Максим Хижинский Lock-free maps
Platonov Sergey
 
PDF
Антон Полухин, Немного о Boost
Sergey Platonov
 
PDF
Zagursky
kuchinskaya
 
PPTX
Developing highload servers with Java
Andrei Pangin
 
Multiprocessor Programming Intro (lecture 3)
Dmitry Tsitelov
 
Java threads - part 2
Nakraynikov Oleg
 
Java threads - part 1
Nakraynikov Oleg
 
разработка серверов и серверных приложений лекция №3
Eugeniy Tyumentcev
 
JavaScript. Event Loop and Timers (in russian)
Mikhail Davydov
 
Mike ponomarenko java17-fork-v1.2
Alex Tumanoff
 
Полухин Антон, Как делать не надо: C++ велосипедостроение для профессионалов
Sergey Platonov
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Sergey Platonov
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Sergey Platonov
 
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Yauheni Akhotnikau
 
10 - Java. Многопоточность в Java: основы
Roman Brovko
 
Wild Async .NET world: AID Kit for boy-scouts
HYS Enterprise
 
Борис Сазонов, RAII потоки и CancellationToken в C++
Sergey Platonov
 
разработка серверов и серверных приложений лекция №2
Eugeniy Tyumentcev
 
Максим Хижинский Lock-free maps
Platonov Sergey
 
Антон Полухин, Немного о Boost
Sergey Platonov
 
Zagursky
kuchinskaya
 
Developing highload servers with Java
Andrei Pangin
 
Ad

Similar to How threads help each other (20)

PDF
Помоги ближнему, или Как потоки помогают друг другу
Alexey Fyodorov
 
PPTX
Deep Dive C# by Sergey Teplyakov
Alex Tumanoff
 
PPTX
PHP basic
Noveo
 
PDF
Многопоточное Программирование - Теория и Практика
Roman Elizarov
 
PDF
Алгоритмы и структуры данных осень 2013 лекция 1
Technopark
 
PDF
Swift School #2
Sergey Pronin
 
PDF
Память в Java. Garbage Collector
Olexandra Dmytrenko
 
PDF
Про асинхронное сетевое программирование
Python Meetup
 
PDF
Статический анализ: ошибки в медиаплеере и безглючная аська
Tatyanazaxarova
 
PPTX
C# Deep Dive
LuxoftTraining
 
PPTX
Algo 00
Alex Tarasov
 
PDF
Discovering Lambdas in Java 8
Stfalcon Meetups
 
PDF
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Roman Elizarov
 
PDF
Statis code analysis
chashnikov
 
PDF
Лекция 6. Стандарт OpenMP
Mikhail Kurnosov
 
PDF
Transpile it.pdf
OlegKlimenko6
 
PPT
Diskretn analiz
strelnikovakr
 
PPT
Diskretn analiz
strelnikovakr
 
PDF
JPoint 2016 - Etudes of DIY Java profiler
Anton Arhipov
 
PPTX
Николай Паламарчук "Functional Programming basics for PHP developers"
Fwdays
 
Помоги ближнему, или Как потоки помогают друг другу
Alexey Fyodorov
 
Deep Dive C# by Sergey Teplyakov
Alex Tumanoff
 
PHP basic
Noveo
 
Многопоточное Программирование - Теория и Практика
Roman Elizarov
 
Алгоритмы и структуры данных осень 2013 лекция 1
Technopark
 
Swift School #2
Sergey Pronin
 
Память в Java. Garbage Collector
Olexandra Dmytrenko
 
Про асинхронное сетевое программирование
Python Meetup
 
Статический анализ: ошибки в медиаплеере и безглючная аська
Tatyanazaxarova
 
C# Deep Dive
LuxoftTraining
 
Algo 00
Alex Tarasov
 
Discovering Lambdas in Java 8
Stfalcon Meetups
 
Теоретический минимум для понимания Java Memory Model (для JPoint 2014)
Roman Elizarov
 
Statis code analysis
chashnikov
 
Лекция 6. Стандарт OpenMP
Mikhail Kurnosov
 
Transpile it.pdf
OlegKlimenko6
 
Diskretn analiz
strelnikovakr
 
Diskretn analiz
strelnikovakr
 
JPoint 2016 - Etudes of DIY Java profiler
Anton Arhipov
 
Николай Паламарчук "Functional Programming basics for PHP developers"
Fwdays
 
Ad

More from Alexey Fyodorov (12)

PDF
Non-blocking Michael-Scott queue algorithm
Alexey Fyodorov
 
PDF
Counter Wars (JEEConf 2016)
Alexey Fyodorov
 
PDF
Non-blocking synchronization — what is it and why we (don't?) need it
Alexey Fyodorov
 
PDF
Синхронизация без блокировок и СМС
Alexey Fyodorov
 
PDF
Общество Мертвых Потоков
Alexey Fyodorov
 
PDF
JDK: CPU, PSU, LU, FR — WTF?!
Alexey Fyodorov
 
PDF
Philosophers
Alexey Fyodorov
 
PDF
Java in Motion
Alexey Fyodorov
 
PDF
Java Platform Tradeoffs (Riga 2013)
Alexey Fyodorov
 
PDF
Java Platform Tradeoffs (CEE SECR 2013)
Alexey Fyodorov
 
PDF
Процесс изменения платформы Java
Alexey Fyodorov
 
PPTX
Java: how to thrive in the changing world
Alexey Fyodorov
 
Non-blocking Michael-Scott queue algorithm
Alexey Fyodorov
 
Counter Wars (JEEConf 2016)
Alexey Fyodorov
 
Non-blocking synchronization — what is it and why we (don't?) need it
Alexey Fyodorov
 
Синхронизация без блокировок и СМС
Alexey Fyodorov
 
Общество Мертвых Потоков
Alexey Fyodorov
 
JDK: CPU, PSU, LU, FR — WTF?!
Alexey Fyodorov
 
Philosophers
Alexey Fyodorov
 
Java in Motion
Alexey Fyodorov
 
Java Platform Tradeoffs (Riga 2013)
Alexey Fyodorov
 
Java Platform Tradeoffs (CEE SECR 2013)
Alexey Fyodorov
 
Процесс изменения платформы Java
Alexey Fyodorov
 
Java: how to thrive in the changing world
Alexey Fyodorov
 

How threads help each other

  • 3. Java-конференции JUG.ru Group Санкт-Петербург 3-4 ноября 2017 Москва 6-7 апреля 2018
  • 4. — Программирование — Алгоритмы — Многопоточность Про что этот доклад
  • 6. Новичкам в области многопоточности Sorry Перейдите в другой зал Про что этот доклад
  • 7. Новичкам в области многопоточности Sorry Перейдите в другой зал Новичкам в неблокирующей синхронизации Короткое введение Про что этот доклад
  • 8. Новичкам в области многопоточности Sorry Перейдите в другой зал Новичкам в неблокирующей синхронизации Короткое введение Продвинутым многопоточным программистам Поговорим о неблокирующей синхронизации Про что этот доклад
  • 10. Модели Модель с разделяемой памятью — Операции на атомарных регистрах: read, write — Удобно программировать, все привыкли
  • 11. Модель с разделяемой памятью — Операции на атомарных регистрах: read, write — Удобно программировать, все привыкли Модель с передачей сообщений — Операции: send, onReceive — Похожа на то, как реально работает железо Модели
  • 12. Преимущества параллелизма — Использование нескольких ядер/процессоров — Да и на 1 ядре тоже! (async I/O)
  • 13. — Использование нескольких ядер/процессоров — Да и на 1 ядре тоже! (async I/O) — Простота моделирования: фреймворк забирает сложность Преимущества параллелизма
  • 14. — Использование нескольких ядер/процессоров — Да и на 1 ядре тоже! (async I/O) — Простота моделирования: фреймворк забирает сложность — Упрощенная обработка асинхронных событий Преимущества параллелизма
  • 15. — Использование нескольких ядер/процессоров — Да и на 1 ядре тоже! (async I/O) — Простота моделирования: фреймворк забирает сложность — Упрощенная обработка асинхронных событий — Более отзывчивые интерфейсы пользователя — Event Dispatch Thread (EDT), async calls Преимущества параллелизма
  • 17. — Взаимоблокировки (Deadlocks) — Инверсия приоритетов Проблемы блокировок
  • 18. — Взаимоблокировки (Deadlocks) — Инверсия приоритетов — Надежность — вдруг владелец блокировки помрет? Проблемы блокировок
  • 19. — Взаимоблокировки (Deadlocks) — Инверсия приоритетов — Надежность — вдруг владелец блокировки помрет? — Performance — Параллелизма в критической секции нет! — Владелец блокировки может быть вытеснен планировщиком Проблемы блокировок
  • 20. Закон Амдала α часть общего объема вычислений, которую нельзя распараллелить 1-α часть, которую можно распараллелить p количество потоков
  • 21. Sp= 𝟏 α# 𝟏%α 𝐩 α часть общего объема вычислений, которую нельзя распараллелить 1-α часть, которую можно распараллелить p количество потоков Закон Амдала
  • 22. Фундаментальные законы природы — XIX век — законы Ньютона
  • 23. Фундаментальные законы природы — XIX век — законы Ньютона — XX век — закон Мура — тактовые частоты — число транзисторов
  • 24. Фундаментальные законы природы — XIX век — законы Ньютона — XX век — закон Мура — тактовые частоты — число транзисторов — XXI век — закон Амдала
  • 25. Фундаментальные законы природы — XIX век — законы Ньютона — XX век — закон Мура — тактовые частоты — число транзисторов — XXI век — закон Амдала Нарушение закона влечет за собой дисциплинарную, гражданско-правовую, административную или уголовную ответственность
  • 26. If-Modify-Write volatile int value = 0; Есть ли в этом коде проблемы в многопоточном окружении? if (value == 0) { value = 42; }
  • 27. If-Modify-Write volatile int value = 0; Нет атомарности if (value == 0) { value = 42; }
  • 28. Compare and Swap int value = 0; LOCK if (value == 0) { value = 42; } UNLOCK
  • 30. Симуляция CAS через synchronized long value; synchronized long get() { return value; } synchronized long compareAndSwap(long expected, long newValue) { long oldValue = value; if (oldValue == expected) { value = newValue; } return oldValue; } synchronized boolean compareAndSet(long expected, long newValue){ return expected == compareAndSwap(expected, newValue); }
  • 31. Compare and Swap — Hardware Support compare-and-swap CAS load-link / store-conditional LL/SC cmpxchg ldrex/strex lwarx/stwcx
  • 32. Atomics AtomicReference • r.get() • r.compareAndSet(v1, v2) • ... AtomicLong • i.get() • i.compareAndSet(42, 43) • i.incrementAndGet(1) • i.getAndAdd(5) • ...
  • 33. Пример. Многопоточный счетчик AtomicLong value = new AtomicLong(); long get() { return value.get(); } void increment() { long v; do { v = value.get(); } while (!value.compareAndSet(v, v + 1)); }
  • 34. Пример. Многопоточный счетчик AtomicLong value = new AtomicLong(); long get() { return value.get(); } void increment() { long v; do { v = value.get(); } while (!value.compareAndSet(v, v + 1)); }
  • 35. Недостатки CAS Contended CAS —> тонны бесполезных CPU-циклов do { v = value.get(); } while (!value.compareAndSet(v, v + 1)); Написание быстрых и корректных алгоритмов на CAS требует экспертизы
  • 36. class Node<E> { final E item; Node<E> next; Node(E item) { this.item = item; } } ...
  • 37. class Node<E> { final E item; Node<E> next; Node(E item) { this.item = item; } } E3 E1 E2
  • 44. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } AtomicReference<Node<E>> top; E3 E1 E2 top
  • 45. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } AtomicReference<Node<E>> top; E3 E1 E2 item top
  • 46. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } E3 E1 E2 item AtomicReference<Node<E>> top; top
  • 47. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } E3 E1 E2 AtomicReference<Node<E>> top; item top
  • 48. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } AtomicReference<Node<E>> top; E3 E1 E2 item top
  • 49. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } AtomicReference<Node<E>> top; E3 E1 E2 item top
  • 50. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } AtomicReference<Node<E>> top; E3 E1 E2 item top
  • 51. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } AtomicReference<Node<E>> top; E3 E1 E2 item top
  • 52. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } E3 E1 E2 AtomicReference<Node<E>> top; top item
  • 53. void push(E item) { Node<E> newHead = new Node<>(item); Node<E> oldHead; do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead)); } E3 E1 E2 AtomicReference<Node<E>> top; top item
  • 54. E pop() { Node<E> newHead; Node<E> oldHead; do { oldHead = top.get(); if (oldHead == null) return null; newHead = oldHead.next; } while (!top.compareAndSet(oldHead, newHead)); return oldHead.item; } E3 E1 E2 top
  • 55. Michael and Scott, 1996 https://www.research.ibm.com/people/m/michael/podc-1996.pdf Потоки помогают друг другу Неблокирующая очередь
  • 56. class LinkedQueue<E> { static class Node<E> { final E item; final AtomicReference<Node<E>> next; Node(E item, AtomicReference<Node<E>> next) { this.item = item; this.next = next; } } final Node<E> dummy = new Node<>(null, null); final AtomicReference<Node<E>> head = new AtomicReference<>(dummy); final AtomicReference<Node<E>> tail = new AtomicReference<>(dummy); }
  • 57. class LinkedQueue<E> { static class Node<E> { final E item; final AtomicReference<Node<E>> next; Node(E item, AtomicReference<Node<E>> next) { this.item = item; this.next = next; } } final Node<E> dummy = new Node<>(null, null); final AtomicReference<Node<E>> head = new AtomicReference<>(dummy); final AtomicReference<Node<E>> tail = new AtomicReference<>(dummy); }
  • 58. class LinkedQueue<E> { private static class Node<E> { final E item; final AtomicReference<Node<E>> next; Node(E item, AtomicReference<Node<E>> next) { this.item = item; this.next = next; } } final Node<E> dummy = new Node<>(null, null); final AtomicReference<Node<E>> head = new AtomicReference<>(dummy); final AtomicReference<Node<E>> tail = new AtomicReference<>(dummy); }
  • 63. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2
  • 64. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 65. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 66. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 67. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 68. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); // tailNext ?= null if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 69. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 70. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 71. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 72. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 73. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { // tailNext != null tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 74. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { // tailNext != null tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 75. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 76. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 77. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 78. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 79. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 80. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 81. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 82. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 83. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 84. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); // true return true; } } } } } tail head dummy 1 2 item
  • 85. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); // true return true; } } } } } tail head dummy 1 2 item
  • 86. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } tail head dummy 1 2 item
  • 87. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); // false return true; } } } } } tail head dummy 1 2 item
  • 88. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); // false return true; } } } } } tail head dummy 1 2 item
  • 89. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); // false return true; } } } } } tail head dummy 1 2 item
  • 90. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { // tailNext == null if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); // false return true; } } } } } tail head dummy 1 2 item
  • 92. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } }
  • 93. public boolean put(E item) { Node<E> newNode = new Node<>(item, null); while (true) { Node<E> currentTail = tail.get(); Node<E> tailNext = currentTail.next.get(); if (currentTail == tail.get()) { if (tailNext != null) { tail.compareAndSet(currentTail, tailNext); } else { if (currentTail.next.compareAndSet(null, newNode)) { tail.compareAndSet(currentTail, newNode); return true; } } } } } АД И ЧЕРТИ (ЗДЕСЬ ДОЛЖНА БЫТЬ КАРТИНКА)
  • 96. void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); } finally { lock.unlock(); } } Как put() сделан в ArrayBlockingQueue
  • 97. void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); } finally { lock.unlock(); } } Как put() сделан в ArrayBlockingQueue
  • 98. void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); } finally { lock.unlock(); } } Как put() сделан в ArrayBlockingQueue
  • 99. void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); } finally { lock.unlock(); } } Как put() сделан в ArrayBlockingQueue
  • 100. void put(E e) throws InterruptedException { checkNotNull(e); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { while (count == items.length) notFull.await(); final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); } finally { lock.unlock(); } } Как put() сделан в ArrayBlockingQueue
  • 102. — Неблокирующие алгоритмы могут быть очень сложными — корректность написанного может быть не очевидна — такие решения очень сложно поддерживать — самописные решения кишат багами — На практике такие решения используют редко — Если ваши ограничения позволяют использовать блокировки, не заморачивайтесь с lock-free Выводы
  • 103. — Неблокирующие алгоритмы могут быть очень сложными — корректность написанного может быть не очевидна — такие решения очень сложно поддерживать — самописные решения кишат багами — На практике такие решения используют редко — Если ваши ограничения позволяют использовать блокировки, не заморачивайтесь с lock-free Выводы
  • 105. Ladan-Mozes, Shavit, 2004, 2008 Идея: избавиться от второго CAS Optimistic Approach http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf
  • 106. Hoffman, Shalev, Shavit, 2007 Baskets Queue http://people.csail.mit.edu/shanir/publications/Baskets%20Queue.pdf
  • 107. — Есть выигрыш в производительности по сравнению с обычной неблокирующей очередью — за это мы платим потерей FIFO-свойства — в реальной жизни «честный» FIFO нужен не всегда — если вам нужно больше перфоманса, им можно пожертвовать. Baskets Queue
  • 110. Материалы • Nitsan Wakart — http://psy-lob-saw.blogspot.com/ • Алексей Шипилёв — https://shipilev.net/ • Максим Хижинский — https://habrahabr.ru/post/219201/