Factory для Thread Safe Singleton в Java
Это образец базового шаблона, который я использовал для Factory, который возвращает потокобезопасный Singleton:
public class UserServiceFactory {
private volatile static UserService userService;
private UserServiceFactory() { }
public static UserService getInstance() {
if (userService == null) {
synchronized(UserServiceImpl.class) {
if (userService == null) {
userService = new UserServiceImpl();
}
}
}
return userService;
}
}
Он использует как volatile, так и идиому двойной проверки, чтобы гарантировать, что один экземпляр создан и виден в потоках.
Есть ли менее подробный и/или менее дорогостоящий способ достижения той же цели в 1.6 +.
Ответы
Ответ 1
Используйте Инициализация по требованию > идиома, это проще и лучше читать:
public class UserServiceFactory {
private UserServiceFactory () {}
private static class UserServiceHolder {
private static final UserService INSTANCE = new UserService();
}
public static UserService getInstance() {
return UserServiceHolder.INSTANCE;
}
}
Однако, я бы предпочел Just Create One идиому.
Обновить: по мере того, как подтверждается история вопроса, вы используете Java EE. Если ваш контейнер поддерживает его, вы также можете сделать его @Singleton
EJB и использовать @EJB
, чтобы ввести его (хотя @Stateless
предпочтительнее, так как @Singleton
по умолчанию заблокирован для чтения).
@Singleton
public class UserService {}
с помощью, например, в управляемом JSF bean
@EJB
private UserService userService;
Таким образом вы делегируете задание на экземпляр контейнера.
Ответ 2
Вы можете позволить загрузчику класса выполнить его maigc и инициализировать статическую переменную при запуске - это гарантированно будет работать, потому что загрузчик классов гарантирует однопоточное поведение.
Если вы хотите инициализировать экземпляр lazily и в большинстве случаев lockfree, тогда нет, вы должны сделать это таким образом и убедитесь, что используете Java >= 1.5
Изменить: см. решение BalusC, которое использует классный загрузчик более разумно. Обратите внимание, что это все работает, потому что classloader инициализирует классы лениво - т.е. они загружаются только при их первом доступе - и потому что внутренние классы обрабатываются так же, как обычные классы в этом отношении (просто потому, что вы загружаете внешний класс, это не значит, что внутренний класс загружен)
Ответ 3
Почему не просто
public synchronized static UserService getInstance() {
if (userService == null) {
userService = new UserServiceImpl();
}
return userService;
}