Thursday, November 26, 2020

Вопросы Java собеседования: Как относится между собой reordering и happens before

На эту тему есть неплохая статья на  https://habr.com/ru/post/133981/ либо можно скачать полную спецификацию работы JMM(java memory model) https://download.oracle.com/otndocs/jcp/memory_model-1.0-pfd-spec-oth-JSpec/

Если вкратце, reodering - это то в каком порядке выполняются операции и доступ потоков, а набор правил описаны в соотношении happens before.

public class Data { int maxAllowedValue; public Data() { this.maxAllowedValue = 9000; } }

public class Keeper { private Data data = null; public Data getData() { if(data == null) { synchronized(this) { if(data == null) { data = new Data(); } } } return data; } }

Поток, который первый обнаружит, что data == null, выполнит следующие действия:

  1. Выделит память под новый объект
  2. Вызовет конструктор класса Data
  3.   Запишет значение 9000 в поле maxAllowedValue класса Data
  4. Запишет только что созданный объект в поле data класса Keeper

Другой поток может увидеть произошедшее в пункте 4 до того, как он увидит произошедшее в пунктах 3,  т.е не в том порядке в которым логично ожидать,  поэтому был создан набор правил,  по которым оптимизатору/компилятору запрещено выполнять reordering. Набор правил описан через соотношение happens before:

1.Освобождение (releasing) монитора happens-before заполучение (acquiring) того же самого монитора

2.Запись в volatile переменную happens-before чтение из той же самой переменной

3. Запись значения в final-поле при конструировании объекта happens-before запись этого объекта в какую-либо переменную, происходящая вне этого конструктора.

Вопросы на собеседовании Java: AtomicInteger vs volitile

Наверняка на собеседовании по многопоточности у вас спросят чем AtomicInteger отличается от volitile. Давайте более подробно раскрою тему.

AtomicInteger гарантирует чтение и атомарность при изменении значения множеством потоков, а volitile гарантирует чтение множеством потока, а изменение значение одним потоком.

Почему это так работает?

AtomicInteger реализует алгоритм CAS(compare and swap), т.е при работе с ячейкой памяти где хранится значение переменной  происходит следующий алгоритм:
1. Прочитать значение 
2. Сравнить с текущим
3. Если отличается перезаписать