Pergunta

Eu tenho uma classe (RInterfaceHL) que chama outra classe (JRIEngine) que fornece métodos nativos em um aplicativo single-threaded. Por isso eu só quero ter uma única instância da minha classe (RInterfaceHL) por JVM.

Eu posso usar o padrão singleton com inicialização estática para garantir que apenas um único instanciação de RInterfaceHL, mas RInterfaceHL precisa construir uma instância de JRIEngine e fornecê-lo com um parâmetro de auto-retorno. Como posso fornecer, de uma forma thread-safe, uma única instância de RInterfaceHL que aceita um parâmetro de auto-retorno para a construção de um único JRIEngine? Estou usando JDK6.

NB:. Este nome semelhante questão não respondeu à minha pergunta

Foi útil?

Solução

A modificação do padrão Singleton que usos Bill Pugh no suporte demanda idioma . Este é o segmento de seguros sem a sobrecarga de construções de linguagem especializadas (ou seja volátil ou sincronizado):

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

Outras dicas

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

    ...
}

EDIT: Devo acrescentar que, se você está construindo coisas para execução em um servlet ou similar recipiente, usando o padrão Singleton puro é provavelmente uma má idéia. Um dos mecanismos IoC injeção / dependência é uma idéia melhor; por exemplo. Primavera como sugerido em outra resposta. Isso permite que você alcance seus "singletons" para o recipiente.

Pode ser um exagero se você está apenas fazendo uma pequena aplicação, mas um quadro de injeção de dependência como Spring Framework pode dar-lhe Singleton comportamento sem a necessidade de construir manualmente e inicializar o objeto estático com a mão.

A injeção de dependência "container" vai construir e fio que você Singleton e suas classes de dependência juntos, e pode ser configurado para fazer o seu objeto uma instância singleton dentro do recipiente.

Há um pouco de uma curva de aprendizado, se você não tiver usado Primavera antes, mas é um quadro muito popular, e provavelmente irá atendê-lo bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top