Domanda

Ho una classe (RInterfaceHL) che chiama un'altra classe (JRIEngine) che fornisce metodi nativi su un'applicazione a thread singolo. Pertanto, voglio solo una singola istanza della mia classe (RInterfaceHL) per JVM.

Posso usare il modello singleton con l'inizializzazione statica per garantire solo una singola istanza di RInterfaceHL, ma RInterfaceHL deve costruire un'istanza di JRIEngine e fornirgli un parametro di loopback. Come posso fornire, in modo thread-safe, una singola istanza di RInterfaceHL che accetta un parametro di loopback per la costruzione di un singolo JRIEngine? Sto usando JDK6.

NB: Questa domanda con un nome simile non risponde alla mia domanda.

È stato utile?

Soluzione

Modifica del modello Singleton che utilizza l'inizializzazione di Bill Pugh su idioma del titolare della domanda . Questo è thread-safe senza il sovraccarico di costrutti linguistici specializzati (ovvero volatile o sincronizzato):

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

Altri suggerimenti

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: dovrei aggiungere che se stai costruendo cose da eseguire in un servlet o in un contenitore simile, usare il modello Singleton puro è probabilmente una cattiva idea. Uno dei meccanismi di iniezione IoC / dipendenza è un'idea migliore; per esempio. Primavera come suggerito in un'altra risposta. Ciò ti consente di valutare i tuoi "singoli" " nel contenitore.

Potrebbe essere eccessivo se stai solo facendo una piccola applicazione, ma un framework di iniezione di dipendenza come Spring Framework può darti un comportamento singleton senza la necessità di costruire manualmente e inizializzare manualmente l'oggetto statico.

L'iniezione di dipendenza "contenitore" costruirà e collegherà singleton e le sue classi di dipendenza insieme, e può essere configurato per rendere il tuo oggetto un'istanza singleton all'interno del contenitore.

C'è un po 'di una curva di apprendimento se non hai mai usato Spring prima, ma è un framework molto popolare e probabilmente ti servirà bene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top