Лекция 5. JMM
JMM делалась давно и не везде было 64 бита. Поэтому long и double не атомарный по железу и тогда надо устраивать синхронизацию. Решили этого не делать и не гарантировать атомарность, хотя в реальности она гарантирована. По JMM доступ атомарен для всех базовых типов, кроме long и double. К volatile long и volatile double атомарен.
На очень старом железе возможно, что напечатает 0xFFFFFFFF00000000:
// before
long l = 0;
// thread 0
l = -1; // 0xFF..F
// thread 1
print(l);
Word tearing
// before
T[] tarr = new T[..];
tarr[0] = tarr[1] = val0;
// thread 0
tarr[0] = val1;
// thread 1
tarr[1] = val1;
// thread 2
join t0, t1;
assert(tarr[0] == tarr[1]);
В JVM обновление двух независимых переменных независимо. Это поддерживается железом.
А что делать с boolean? Будем делать boolean 1 байт. Если хотим эффективные биты, то можно использовать BitSet. Но тогда нет гарантий про word tearing и в аналогичном коде assert может упасть (см. выше)
Модель памяти это компромисс между удобством программирования, сложностью реализации языка и поддержки со стороны железа.
Последовательная согласованность
В целом мы хотим иметь последовательную согласованность, чтобы можно было анализировать чередованием. Но это усложняет локальные оптимизации.
По JMM, если в исполнении нет гонок (нет параллельных конфликтующих операций), то оно последовательно согласованно. Но что такое "параллельно" с точки зрения JMM?
Program order
Программный порядок связывает действия внутри одного потока.
Synchronization actions
Это:
volatileчтение/запись- Взятие/отпускание блокировки
- Первое и последнее действие в потоке
- Запуск потока
- Действия, обнаруживающее останов потока
Все synchronization actions образую synchronization order. Это линейный порядок и он консистентен с program order. Все чтения в synchronization order видят последние записи в synchronization order.
Synchronizes-with order
SO это очень мощно, мы будем использовать SW - подпорядок SO, ограниченный парными действиями синхронизации, например volatile read/write над одной и той же переменной.
Happens-before order
\(HB = (SW \cup PO)^+\)
Чтения видят либо последнюю запись по HB, либо что-то еще под гонкой.
Final семантика
В конце конструктора происходит freeze action, которое "замораживает" поля.