Многопоточность Java
Под Капотом

#volatile #membar #dragons #openjdk

#jmm #store #load #internals

#cachecoherency #omg

#jeeconf #jeeconf2014

#jugru #jugua

Gleb Smirnov

me@gvsmirnov.ru

Текущие Абстракции

void executedOnCpu0() {
    value = 10;
    finished = true;
}
void executedOnCpu1() {
    while(!finished);
    assert value == 10;
}

Может ли упасть?

А на x86?

Подход Теоретический

Так Говорила JMM

Запись значения в volatile поле

happens-before

Последующее чтение значения из этого поля

Запись значения в volatile поле

synchronizes-with

Последующее чтение значения из этого поля


Чувак, я ничего не понял из того, что ты сейчас сказал.
Но ты заговорил и достучался моего до сердца.

Подход Эмпирический

Не Сотвори Себе Велосипеда

Демо jcstress

Результаты Теста (x86)

  • y: 0, x: 3
  • y: 0, x: 0
  • y: 1, x: 0
  • y: 1, x: 3

Результаты Теста (ARM)

  • y: 0, x: 3
  • y: 0, x: 0
  • y: 1, x: 0
  • y: 1, x: 3

Результаты Теста (x86, C1)

  • y: 0, x: 3
  • y: 0, x: 0
  • y: 1, x: 0
  • y: 1, x: 3

Куда Протекли Абстракции?

void executedOnCpu0() {
    value = 10;
    finished = true;
}

Никто Не Хочет Быть Тормозом

  • На каждом слое могут применяться оптимизации
  • Есть оптимизации, меняющие наблюдаемое поведение
  • Иногда это вполне допустимо (но иногда — нет)
  • Разработчики среды не знают этого заранее

Когерентность Кешей, Например

Variable Cached Value
finished false
value N/A
Variable Cached Value
finished N/A
value 0
value = 10;

  • — Удали из кеша!
  • ...
  • ...
  • — Я удалил!

finished = true;
  • Пришлось ждать очень долго
  • Процессор простаивал
  • Вы — самое слабое звено!
  • Время оптимизировать

value = 10;

(исполняется асинхронно)

finished = true;

(тоже)

Модель Памяти — Нужна

Барьер Памяти

void foo() {
    value = 10;
    magicUnicorn();
    finished = true;
}

void foo() {
    finished = true;
    magicUnicorn();
    value = 10;
}

(С точки зрения другого потока)

Есть Два Типа Операций с Памятью

Запись
Чтение
Store
Load
ST
LD

Не Всех Под Одну Гребёнку

For X, Y in [Store, Load]:

Все операции типа X перед барьером типа XY

завершатся до того, как

Начнётся любая операция типа Y после этого барьера

Так Делать Нельзя

Зато Можно Так

Семантика Acquire и Release

The source of a synchronizes-with edge is called a release, and the destination is called an acquire

Источником синхронизирует-с краю называется релиз, и назначения называется приобретают

vstore(a.f, 1)
\---(sync-with)--->
vread(a.f, 1)

(исходник)

javac

(байт-код)

Frontend

(HIR)

JIT-Оптимизатор

(LIR)

Backend

(нативный код)

...

(???)

PROFIT!

LIVE DEMO TIME!

Полезные Материалы