У меня есть общий интерфейс для ряда одноэлементных реализаций.Интерфейс определяет метод инициализации, который может генерировать проверенное исключение.
Мне нужна фабрика, которая будет возвращать кэшированные одноэлементные реализации по требованию, и интересно, является ли следующий подход поточно-ориентированным?
UPDATE1: Пожалуйста, не предлагайте какие-либо третьи частично библиотеки, так как для этого потребуется получить юридическое разрешение из-за возможных проблем с лицензированием: -)
ОБНОВЛЕНИЕ2: этот код, вероятно, будет использоватьсяв среде EJB, поэтому предпочтительнее не создавать дополнительные потоки или использовать подобные вещи.
interface Singleton
{
void init() throws SingletonException;
}
public class SingletonFactory
{
private static ConcurrentMap<String, AtomicReference<? extends Singleton>> CACHE =
new ConcurrentHashMap<String, AtomicReference<? extends Singleton>>();
public static <T extends Singleton> T getSingletonInstance(Class<T> clazz)
throws SingletonException
{
String key = clazz.getName();
if (CACHE.containsKey(key))
{
return readEventually(key);
}
AtomicReference<T> ref = new AtomicReference<T>(null);
if (CACHE.putIfAbsent(key, ref) == null)
{
try
{
T instance = clazz.newInstance();
instance.init();
ref.set(instance); // ----- (1) -----
return instance;
}
catch (Exception e)
{
throw new SingletonException(e);
}
}
return readEventually(key);
}
@SuppressWarnings("unchecked")
private static <T extends Singleton> T readEventually(String key)
{
T instance = null;
AtomicReference<T> ref = (AtomicReference<T>) CACHE.get(key);
do
{
instance = ref.get(); // ----- (2) -----
}
while (instance == null);
return instance;
}
}
Я не совсем уверен в строках (1) и (2).Я знаю, что указанный объект объявлен как изменчивое поле в AtomicReference
, и, следовательно, изменения, сделанные в строке (1), должны сразу же стать видимыми в строке (2) - но все же есть некоторые сомнения ...
Кромеэто - я думаю, что использование ConcurrentHashMap
решает проблему атомарности помещения нового ключа в кеш.
Ребята, видите ли вы какие-либо проблемы с этим подходом?Спасибо!
PS: Я знаю об идиоме класса статического держателя - и я не использую его из-за ExceptionInInitializerError
(в который включается любое исключение, возникающее при создании экземпляра singleton) и последующие NoClassDefFoundError
, которые я не хочу поймать.Вместо этого я хотел бы использовать преимущество выделенного проверенного исключения, перехватывая его и обрабатывая его изящно, а не анализируя трассировку стека EIIR или NCDFE.