Как не надо синхронизировать factory method

На так давно мне рассказали о достаточно интересной методике double-checked locking и я естественно ринулся в бой применяя ее, буквально все синхронизированный синглтоны были заменены на двойные if. Прирост производительности оказался действительно вполне ощутим.

И узнал я об этой технике как раз вовремя, ибо очень пригодилось. В один прекрасный момент после окончания очередного куска девелопмента наше web-приложение стало ощутимо тормозить, после прочтения трассы стека стало ясно, что большинство ajp-http потоков ожидают получения блокировки на строке 8 следующего участка кода (естественно оригинал был несколько сложнее):
Казалось бы безопасный участок кода, ан нет, не все так просто. Строка 5 не синхронизированна и потоки могут получить к ней доступ одновременно. Внутри HashMap оперирует сразу несколькими массивами для того, что бы ускорить поиск данных. И соответственно, если исполнение метода get будет прервано, а затем возобновлено на выходе будут не верные данные или того лучше исключение, аля IndexOutOfBoundException. После чего, если потоку все таки повезло и вместо не валидных данных и исключений он получал null, тот благополучно отправлялся к собратьям на блокировку. Уж не знаю почему, но моим потокам везло.
Безусловно автор хотел ускорить работу метода для случая, если сервис уже инициализирован. Но делать это следует несколько окуратнее:
Как видно вызов метода get будет выполняться почти так же быстро как и без блокировки, ибо все потоки пытающиеся читать будут делать это одновременно, но если появиться поток, которому нужно вызвать put "читателям" придется обождать.
В свою очередь, если искомый сервис еще не существует устанавливается блокировка записи (все читатели тормозятся), проверяется не появился ли сервис пока мы блокировались, сервис создается и все потоки разблокируются.