質問
私は、シングルスレッドアプリケーションでネイティブメソッドを提供する別のクラス(JRIEngine)を呼び出すクラス(RInterfaceHL)を持っています。したがって、JVMごとにクラスの単一インスタンス(RInterfaceHL)のみが必要です。
静的初期化でシングルトンパターンを使用して、RInterfaceHLのインスタンス化を1つだけにすることができますが、RInterfaceHLはJRIEngineのインスタンスを構築し、ループバックパラメーターを指定する必要があります。単一のJRIEngineを構築するためのループバックパラメーターを受け入れるRInterfaceHLの単一インスタンスをスレッドセーフな方法で提供するにはどうすればよいですか? JDK6を使用しています。
NB:これの同様の名前の質問は私の質問に答えません。
解決
ビルピューのオンデマンドホルダーイディオムの初期化を使用するシングルトンパターンの変更。これは、特殊な言語構成要素(つまり、volatileまたはsynchronized)のオーバーヘッドなしでスレッドセーフです:
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));
}
...
}
編集:サーブレットなどのコンテナで実行するものを構築する場合、純粋なシングルトンパターンを使用するのはおそらく悪い考えです。 IoC /依存性注入メカニズムの1つはより良いアイデアです。例えば別の回答で示唆されているように春。これにより、「シングルトン」を範囲指定できます。コンテナに。
小規模なアプリケーションを実行しているだけではやり過ぎかもしれませんが、 Spring Frameworkのような依存性注入フレームワークは、静的オブジェクトを手動で手動で構築および初期化する必要なく、シングルトン動作を提供できます。
依存性注入" container"シングルトンとその依存関係クラスを構築して配線し、オブジェクトをコンテナ内のシングルトンインスタンスにするように構成できます。
Springを使用したことがない場合は少し学習曲線がありますが、これは非常に人気のあるフレームワークであり、おそらく役立つでしょう。