Фабрика для Потокобезопасного Синглтона в Java - PullRequest
10 голосов
/ 22 мая 2011

Это образец базового шаблона, который я использовал для фабрики, которая возвращает поточно-ориентированный синглтон:

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 +.

Ответы [ 3 ]

19 голосов
/ 22 мая 2011

Используйте Держатель инициализации по требованию идиома, это проще и лучше читается:

public class UserServiceFactory {

    private UserServiceFactory () {}

    private static class UserServiceHolder {
        private static final UserService INSTANCE = new UserService();
    }

    public static UserService getInstance() {
        return UserServiceHolder.INSTANCE;
    }

}

Однако я бы предпочел Просто создайте один идиома.


Обновление : как показывает история ваших вопросов, вы используете Java EE. Если ваш контейнер поддерживает его, вы также можете сделать его @Singleton EJB и использовать @EJB для его внедрения (хотя @Stateless предпочтительнее, поскольку @Singleton по умолчанию заблокировано для чтения).

@Singleton
public class UserService {}

например в управляемом компоненте JSF

@EJB
private UserService userService;

Таким образом, вы делегируете задание создания экземпляра в контейнер.

3 голосов
/ 22 мая 2011

Вы можете позволить загрузчику классов выполнить его maigc и инициализировать статическую переменную при запуске - это гарантированно сработает, потому что загрузчик классов гарантирует однопоточное поведение.

Если вы хотите инициализировать экземпляр лениво и в основномlockfree, то нет, вы должны сделать это таким образом и убедиться, что вы используете Java> = 1.5

Редактировать: посмотрите решение BalusC, которое использует загрузчик классов немного более разумно.Обратите внимание, что все это работает, потому что загрузчик классов инициализирует классы лениво - то есть они загружаются только при первом обращении - и потому что внутренние классы обрабатываются так же, как обычные классы в этом отношении (просто потому, что вы загружаете внешний класс, это не означает, что внутреннийкласс загружен)

0 голосов
/ 22 мая 2011

Почему не просто

public synchronized static UserService getInstance() {
    if (userService == null) {
        userService = new UserServiceImpl();    
    }
    return userService;
}
...