Pregunta

Tengo una clase (RInterfaceHL) que llama a otra clase (JRIEngine) que proporciona métodos nativos en una aplicación de un solo hilo. Por lo tanto, solo quiero tener una instancia única de mi clase (RInterfaceHL) por JVM.

Puedo usar el patrón de singleton con inicialización estática para asegurar solo una instanciación única de RInterfaceHL, pero RInterfaceHL necesita construir una instancia de JRIEngine y suministrarla con un parámetro de loopback. ¿Cómo puedo proporcionar, de manera segura para subprocesos, una única instancia de RInterfaceHL que acepte un parámetro de bucle de retorno para la construcción de una única JRIEngine? Estoy usando JDK6.

NB: Esta pregunta de nombre similar no responde a mi pregunta.

¿Fue útil?

Solución

Modificación del patrón Singleton que utiliza Inicialización de Bill Pugh en el idioma del titular de la demanda . Esto es seguro para subprocesos sin la sobrecarga de construcciones de lenguaje especializadas (es decir, volátiles o sincronizadas):

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

Otros consejos

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

    ...
}

EDITAR: Debo agregar que si está construyendo cosas para ejecutar en un servlet o contenedor similar, el uso del patrón Singleton puro es probablemente una mala idea. Uno de los mecanismos de inyección de IoC / dependencia es una mejor idea; p.ej. Primavera como se sugiere en otra respuesta. Esto le permite ver su " singletons " al contenedor.

Puede ser excesivo si solo está haciendo una aplicación pequeña, pero un marco de inyección de dependencia como Spring Framework puede darte un comportamiento de singleton sin la necesidad de construir e inicializar manualmente el objeto estático a mano.

La inyección de dependencia " contenedor " construirá y te conectará singleton y sus clases de dependencia, y se puede configurar para que tu objeto sea una instancia de singleton dentro del contenedor.

Hay una pequeña curva de aprendizaje si no has usado Spring antes, pero es un marco muy popular y probablemente te sirva bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top