111
Как потоки помогают друг другу Алексей Федоров, JUG.ru

How threads help each other

Embed Size (px)

Citation preview

Page 1: How threads help each other

Какпотокипомогаютдругдругу

Алексей Федоров, JUG.ru

Page 2: How threads help each other

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

about.me/23derevo

Page 3: How threads help each other

Java-конференции JUG.ru Group

Санкт-Петербург3-4 ноября 2017

Москва6-7 апреля 2018

Page 4: How threads help each other

� Программирование� Алгоритмы� Многопоточность

Про что этот доклад

Page 5: How threads help each other

Много интересных докладов в других залах

Page 6: How threads help each other

Новичкам в области многопоточности

SorryПерейдите в другой зал

Про что этот доклад

Page 7: How threads help each other

Новичкам в области многопоточности

SorryПерейдите в другой зал

Новичкам в неблокирующей синхронизации

Короткое введение

Про что этот доклад

Page 8: How threads help each other

Новичкам в области многопоточности

SorryПерейдите в другой зал

Новичкам в неблокирующей синхронизации

Короткое введение

Продвинутым многопоточным программистам

Поговорим о неблокирующейсинхронизации

Про что этот доклад

Page 9: How threads help each other

Модели

Page 10: How threads help each other

Модели

Модель с разделяемой памятью� Операции на атомарных регистрах: read, write� Удобно программировать, все привыкли

Page 11: How threads help each other

Модель с разделяемой памятью� Операции на атомарных регистрах: read, write� Удобно программировать, все привыкли

Модель с передачей сообщений� Операции: send, onReceive� Похожа на то, как реально работает железо

Модели

Page 12: How threads help each other

Преимущества параллелизма

� Использование нескольких ядер/процессоров� Да и на 1 ядре тоже! (async I/O)

Page 13: How threads help each other

� Использование нескольких ядер/процессоров� Да и на 1 ядре тоже! (async I/O)

� Простота моделирования: фреймворк забирает сложность

Преимущества параллелизма

Page 14: How threads help each other

� Использование нескольких ядер/процессоров� Да и на 1 ядре тоже! (async I/O)

� Простота моделирования: фреймворк забирает сложность

� Упрощенная обработка асинхронных событий

Преимущества параллелизма

Page 15: How threads help each other

� Использование нескольких ядер/процессоров� Да и на 1 ядре тоже! (async I/O)

� Простота моделирования: фреймворк забирает сложность

� Упрощенная обработка асинхронных событий

� Более отзывчивые интерфейсы пользователя� Event Dispatch Thread (EDT), async calls

Преимущества параллелизма

Page 16: How threads help each other

Проблемы блокировок� Взаимоблокировки (Deadlocks)

Page 17: How threads help each other

� Взаимоблокировки (Deadlocks)� Инверсия приоритетов

Проблемы блокировок

Page 18: How threads help each other

� Взаимоблокировки (Deadlocks)� Инверсия приоритетов� Надежность

� вдруг владелец блокировки помрет?

Проблемы блокировок

Page 19: How threads help each other

� Взаимоблокировки (Deadlocks)� Инверсия приоритетов� Надежность

� вдруг владелец блокировки помрет?� Performance

� Параллелизма в критической секции нет!� Владелец блокировки может быть вытеснен планировщиком

Проблемы блокировок

Page 20: How threads help each other

Закон Амдалаα часть общего объема вычислений,

которую нельзя распараллелить

1-α часть, которую можно распараллелить

p количество потоков

Page 21: How threads help each other

Sp=𝟏

α#𝟏%α𝐩

α часть общего объема вычислений, которую нельзя распараллелить

1-α часть, которую можно распараллелить

p количество потоков

Закон Амдала

Page 22: How threads help each other

Фундаментальные законы природы� XIX век — законы Ньютона

Page 23: How threads help each other

Фундаментальные законы природы� XIX век — законы Ньютона

� XX век — закон Мура� тактовые частоты� число транзисторов

Page 24: How threads help each other

Фундаментальные законы природы� XIX век — законы Ньютона

� XX век — закон Мура� тактовые частоты� число транзисторов

� XXI век — закон Амдала

Page 25: How threads help each other

Фундаментальные законы природы� XIX век — законы Ньютона

� XX век — закон Мура� тактовые частоты� число транзисторов

� XXI век — закон Амдала

Нарушение закона влечет за собой дисциплинарную, гражданско-правовую, административную или уголовную ответственность

Page 26: How threads help each other

If-Modify-Write

volatile int value = 0;

Есть ли в этом коде проблемы в многопоточном окружении?

if (value == 0) {value = 42;

}

Page 27: How threads help each other

If-Modify-Write

volatile int value = 0;

Нет атомарностиif (value == 0) {

value = 42;}

Page 28: How threads help each other

Compare and Swap

int value = 0;

LOCKif (value == 0) {

value = 42;}

UNLOCK

Page 29: How threads help each other

Введем волшебную операцию

value.compareAndSet(0, 42);

int value = 0;

Page 30: How threads help each other

Симуляция CAS через synchronizedlong 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);

}

Page 31: How threads help each other

Compare and Swap — Hardware Support

compare-and-swapCAS

load-link / store-conditionalLL/SC

cmpxchg ldrex/strex lwarx/stwcx

Page 32: How threads help each other

Atomics

AtomicReference• r.get()

• r.compareAndSet(v1, v2)

• ...

AtomicLong• i.get()

• i.compareAndSet(42, 43)

• i.incrementAndGet(1)

• i.getAndAdd(5)

• ...

Page 33: How threads help each other

Пример. Многопоточный счетчик

AtomicLong value = new AtomicLong();

long get() {return value.get();

}

void increment() {long v;do {

v = value.get();} while (!value.compareAndSet(v, v + 1));

}

Page 34: How threads help each other

Пример. Многопоточный счетчик

AtomicLong value = new AtomicLong();

long get() {return value.get();

}

void increment() {long v;do {

v = value.get();} while (!value.compareAndSet(v, v + 1));

}

Page 35: How threads help each other

Недостатки CAS

Contended CAS —> тонны бесполезных CPU-циклов

do {v = value.get();

} while (!value.compareAndSet(v, v + 1));

Написание быстрых и корректных алгоритмов на CAS требует экспертизы

Page 36: How threads help each other

class Node<E> {

final E item;

Node<E> next;

Node(E item) {this.item = item;

}

}

...

Page 37: How threads help each other

class Node<E> {

final E item;

Node<E> next;

Node(E item) {this.item = item;

}

}

E3

E1

E2

Page 38: How threads help each other

E3

E1

E2

top

Page 39: How threads help each other

E3

E1

E2

top

item1

Thread 1

Page 40: How threads help each other

E3

E1

E2

top

item1

Thread 1

Page 41: How threads help each other

E3

E1

E2

top

item2item1

Thread 1 Thread 2

Page 42: How threads help each other

E3

E1

E2

top

item2item1

Thread 1 Thread 2

Page 43: How threads help each other

E3

E1

E2

top

item2item1

Thread 1 Thread 2

Page 44: How threads help each other

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

Page 45: How threads help each other

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

Page 46: How threads help each other

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

Page 47: How threads help each other

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

Page 48: How threads help each other

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

Page 49: How threads help each other

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

Page 50: How threads help each other

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

Page 51: How threads help each other

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

Page 52: How threads help each other

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

Page 53: How threads help each other

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

Page 54: How threads help each other

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

Page 55: How threads help each other

Michael and Scott, 1996https://www.research.ibm.com/people/m/michael/podc-1996.pdf

Потоки помогают друг другу

Неблокирующаяочередь

Page 56: How threads help each other

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);

}

Page 57: How threads help each other

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);

}

Page 58: How threads help each other

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);

}

Page 59: How threads help each other

tailhead

dummy 1 2

Page 60: How threads help each other

tailhead

dummy 1 2 3

Page 61: How threads help each other

tailhead

dummy 1 2 3

Page 62: How threads help each other

tailhead

dummy 1 2 3

Page 63: How threads help each other

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

Page 64: How threads help each other

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

Page 65: How threads help each other

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

Page 66: How threads help each other

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

Page 67: How threads help each other

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

Page 68: How threads help each other

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 ?= nullif (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

Page 69: How threads help each other

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

Page 70: How threads help each other

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

Page 71: How threads help each other

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

Page 72: How threads help each other

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

Page 73: How threads help each other

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 != nulltail.compareAndSet(currentTail, tailNext);

} else {if (currentTail.next.compareAndSet(null, newNode)) {

tail.compareAndSet(currentTail, newNode);return true;

}}

}}

}

tail

head

dummy 1 2 item

Page 74: How threads help each other

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 != nulltail.compareAndSet(currentTail, tailNext);

} else {if (currentTail.next.compareAndSet(null, newNode)) {

tail.compareAndSet(currentTail, newNode);return true;

}}

}}

}

tail

head

dummy 1 2 item

Page 75: How threads help each other

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

Page 76: How threads help each other

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

Page 77: How threads help each other

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

Page 78: How threads help each other

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

Page 79: How threads help each other

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

Page 80: How threads help each other

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

Page 81: How threads help each other

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

Page 82: How threads help each other

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

Page 83: How threads help each other

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

Page 84: How threads help each other

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); // truereturn true;

}}

}}

}

tail

head

dummy 1 2 item

Page 85: How threads help each other

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); // truereturn true;

}}

}}

}

tail

head

dummy 1 2 item

Page 86: How threads help each other

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

Page 87: How threads help each other

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); // falsereturn true;

}}

}}

}

tail

head

dummy 1 2 item

Page 88: How threads help each other

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); // falsereturn true;

}}

}}

}

tail

head

dummy 1 2 item

Page 89: How threads help each other

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); // falsereturn true;

}}

}}

}

tail

head

dummy 1 2 item

Page 90: How threads help each other

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); // falsereturn true;

}}

}}

}

tail

head

dummy 1 2 item

Page 91: How threads help each other

Что получилось

Page 92: How threads help each other

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;

}}

}}

}

Page 93: How threads help each other

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;

}}

}}

} АД И ЧЕРТИ (ЗДЕСЬ ДОЛЖНА БЫТЬ КАРТИНКА)

Page 94: How threads help each other

ArrayBlockingQueue

Page 95: How threads help each other

ArrayBlockingQueue

0 1 2 3 4 N-1...

Page 96: How threads help each other

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

Page 97: How threads help each other

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

Page 98: How threads help each other

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

Page 99: How threads help each other

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

Page 100: How threads help each other

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

Page 101: How threads help each other

Выводы

Page 102: How threads help each other

� Неблокирующие алгоритмы могут быть очень сложными� корректность написанного может быть не очевидна� такие решения очень сложно поддерживать� самописные решения кишат багами

� На практике такие решения используют редко

� Если ваши ограничения позволяют использовать блокировки, не заморачивайтесь с lock-free

Выводы

Page 103: How threads help each other

� Неблокирующие алгоритмы могут быть очень сложными� корректность написанного может быть не очевидна� такие решения очень сложно поддерживать� самописные решения кишат багами

� На практике такие решения используют редко

� Если ваши ограничения позволяют использовать блокировки, не заморачивайтесь с lock-free

Выводы

Page 104: How threads help each other

Модификации

Page 105: How threads help each other

Ladan-Mozes, Shavit, 2004, 2008Идея:избавитьсяотвторогоCAS

OptimisticApproach

http://people.csail.mit.edu/edya/publications/OptimisticFIFOQueue-journal.pdf

Page 106: How threads help each other

Hoffman, Shalev, Shavit, 2007

BasketsQueue

http://people.csail.mit.edu/shanir/publications/Baskets%20Queue.pdf

Page 107: How threads help each other

� Есть выигрыш в производительности по сравнению с обычной неблокирующей очередью

� за это мы платим потерей FIFO-свойства

� в реальной жизни «честный» FIFO нужен не всегда� если вам нужно больше перфоманса, им можно пожертвовать.

Baskets Queue

Page 108: How threads help each other

Материалы

Page 109: How threads help each other

Литература

Page 110: How threads help each other

Материалы

• Nitsan Wakart — http://psy-lob-saw.blogspot.com/• АлексейШипилёв — https://shipilev.net/• МаксимХижинский — https://habrahabr.ru/post/219201/

Page 111: How threads help each other

Вопросы и ответы