Вопрос

У меня есть класс (RInterfaceHL), который вызывает другой класс (JRIEngine), который предоставляет собственные методы в однопоточном приложении.Поэтому я хочу иметь только один экземпляр моего класса (RInterfaceHL) для каждой JVM.

Я могу использовать одноэлементный шаблон со статической инициализацией, чтобы обеспечить только один экземпляр RInterfaceHL, но RInterfaceHL должен создать экземпляр JRIEngine и предоставить ему параметр обратной связи.Как я могу потокобезопасным способом предоставить единственный экземпляр RInterfaceHL, который принимает параметр обратной связи для построения одного JRIEngine?Я использую JDK6.

Примечание: Это вопрос с аналогичным названием не отвечает на мой вопрос.

Это было полезно?

Решение

Модификация шаблона Singleton, который использует инициализацию Билла Пью по требованию идиомы владельца запроса . Это потокобезопасно без дополнительных затрат на специализированные языковые конструкции (то есть volatile или синхронизированные):

public final class RInterfaceHL {

    /**
     * Private constructor prevents instantiation from other classes.
     */
    private RInterfaceHL() { }

    /**
     * R REPL (read-evaluate-parse loop) handler.
     */
    private static RMainLoopCallbacks rloopHandler = null;

    /**
     * SingletonHolder is loaded, and the static initializer executed, 
     * on the first execution of Singleton.getInstance() or the first 
     * access to SingletonHolder.INSTANCE, not before.
     */
    private static final class SingletonHolder {

        /**
         * Singleton instance, with static initializer.
         */
        private static final RInterfaceHL INSTANCE = initRInterfaceHL();

        /**
         * Initialize RInterfaceHL singleton instance using rLoopHandler from
         * outer class.
         * 
         * @return RInterfaceHL instance
         */
        private static RInterfaceHL initRInterfaceHL() {
            try {
                return new RInterfaceHL(rloopHandler);
            } catch (REngineException e) {
                // a static initializer cannot throw exceptions
                // but it can throw an ExceptionInInitializerError
                throw new ExceptionInInitializerError(e);
            }
        }

        /**
         * Prevent instantiation.
         */
        private SingletonHolder() {
        }

        /**
         * Get singleton RInterfaceHL.
         * 
         * @return RInterfaceHL singleton.
         */
        public static RInterfaceHL getInstance() {
            return SingletonHolder.INSTANCE;
        }

    }

    /**
     * Return the singleton instance of RInterfaceHL. Only the first call to
     * this will establish the rloopHandler.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @return RInterfaceHL singleton instance
     * @throws REngineException
     *             if REngine cannot be created
     */
    public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
            throws REngineException {
        RInterfaceHL.rloopHandler = rloopHandler;

        RInterfaceHL instance = null;

        try {
            instance = SingletonHolder.getInstance();
        } catch (ExceptionInInitializerError e) {

            // rethrow exception that occurred in the initializer
            // so our caller can deal with it
            Throwable exceptionInInit = e.getCause();
            throw new REngineException(null, exceptionInInit.getMessage());
        }

        return instance;
    }

    /**
     * org.rosuda.REngine.REngine high level R interface.
     */
    private REngine rosudaEngine = null;

    /**
     * Construct new RInterfaceHL. Only ever gets called once by
     * {@link SingletonHolder.initRInterfaceHL}.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @throws REngineException
     *             if R cannot be loaded.
     */
    private RInterfaceHL(RMainLoopCallbacks rloopHandler)
            throws REngineException {

        // tell Rengine code not to die if it can't
        // load the JRI native DLLs. This allows
        // us to catch the UnsatisfiedLinkError
        // ourselves
        System.setProperty("jri.ignore.ule", "yes");

        rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
    }
}

Другие советы

public class RInterfaceHL {
    private static RInterfaceHL theInstance;

    private final JRIEngine engine;

    private RInterfaceHL(JRIEngine engine) {
        this.engine = engine;
    }

    public static synchronized RInterfaceHL getInstance() {
        if (theInstance == null) {
            throw new IllegalStateException("not initialized");
        }
        return theInstance;
    }
    public static synchronized void initialize(String loopback) {
        if (theInstance != null) {
            throw new IllegalStateException("already initialized");
        }
        theInstance = new RInterfaceHL(new JRIEngine(loopback));
    }

    ...
}

РЕДАКТИРОВАТЬ: я должен добавить, что если вы создаете материал для запуска в сервлете или подобном контейнере, использование чистого шаблона Singleton, вероятно, является плохой идеей. Один из механизмов IoC / внедрения зависимостей - лучшая идея; например Весна, как это предлагается в другом ответе. Это позволяет вам охватить ваши " синглтоны " в контейнер.

Это может быть излишеством, если вы просто делаете небольшое приложение, но фреймворк внедрения зависимостей, такой как Пружинный каркас может предоставить вам одноэлементное поведение без необходимости вручную создавать и инициализировать статический объект вручную.

"Контейнер" внедрения зависимостей создаст и свяжет ваш синглтон и его классы зависимостей вместе и может быть сконфигурирован так, чтобы сделать ваш объект экземпляром singleton внутри контейнера.

Если вы раньше не использовали Spring, вам придется немного повозиться, но это действительно популярный фреймворк, и, вероятно, он сослужит вам хорошую службу.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top