HotLog

пятница, 1 октября 2010 г.

Double-check locking.

Идея довольна проста. Пусть у нас есть Singleton, который дорог в создании. Создавать его может потребоваться, а может и нет. Хорошая идея создавать в тот момент, когда он понадобился в первый раз. Получаем код вроде такого:



Обратите внимание на synchonized при декларации метода: каждый раз, когда понадобится ссылка на Singleton, потоку придется захватить монитор, а это весьма дорогостоящая операция.
Если обращение к Singleton'у происходит часто, хотелось бы отказаться от захвата монитора в том случае, если объект уже создан и требуется только чтение.

Неправильное, но интуитивное решение:



Почему же оно неправильное? Причина кроется в специфике Java Memory Model, а именно в том, что обычные (не-volatile) переменные не являются точкой синхронизации.

Пусть у Singletona есть важное для нас поле value:



В худшем случае может происходить следующее:


NNпервый потоквторой поток
1 - getSingleton()
2 - singleton = new Singleton() и value=42
3 - переменная singleton скидывается в основную память
4getSingleton() -
5так как переменная singleton не null, в synchronized блок не заходим
6singleton.value==0 (значение по умолчанию, так как новое значение не было записано еще в основную память)-

Все потому, что для не-volatile переменных не гарантируется, что "happens-before order". Нужна некая точка синхронизации. Так как мы хотим отказаться от использования мониторов, то решение: пометить как volatile переменную singleton.