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;
}