Согласен с предыдущими постами Тима. Волатильность делает для видимости, и причина двойной проверки блокировки была описана как умная, но сломанная вокруг частично построенных объектов (согласованность кэша / оптимизация JVM).
Это все в книге Гетца, как предлагает Тим, но я хотел поднять вопрос о ленивой инициализации. Зачем это делать? По моему опыту, как правило, он не нужен, и вы работаете в многопоточном контексте и действительно заботитесь о безопасности инициализации - вы ввели много вариантов и сложностей, которые действительно трудно протестировать.
Я бы также подчеркнул старое предупреждение, чтобы не оптимизировать рано. Знаете ли вы, что грубая синхронизация замедляет работу приложения? Обычно медленная конкуренция блокировок, а не ключевое слово syncrhonization per-sa. Быстрый тест с syncrhonized и DCL подтвердит.