私のプログラムはJava Scripting APIを使用し、いくつかのスクリプトを同時に評価できます。共有スクリプトオブジェクト、バインディング、またはコンテキストは使用しませんが、同じScriptEngine
とCompiledScript
オブジェクト:Java 8のOracle Nashorn実装はマルチスレッドではありません。ScriptEngineFactory.getParameter('THREADING')
はドキュメントに記載されているnull
を返します。
エンジンの実装はスレッドセーフではないため、複数のスレッドで同時にスクリプトを実行することはできません。
スレッドごとにScriptEngine
の個別のインスタンスを作成する必要があるということですか?その上、ドキュメントにはCompiledScript
の同時使用について何も書かれていませんが、
各CompiledScriptはScriptEngineに関連付けられています
CompiledScript
スレッドセーフは関連するScriptEngine
に依存すると想定できます。つまり、Nashornを使用するスレッドごとに個別のCompiledScript
インスタンスを使用する必要があります。
必要な場合、ThreadLocal
、プールなどを使用して、この(私は非常に一般的だと思う)ケースの適切な解決策は何ですか?
final String script = "...";
final CompiledScript compiled = ((Compilable)scriptEngine).compile(script);
for (int i=0; i<50; i++) {
Thread thread = new Thread () {
public void run() {
try {
scriptEngine.eval(script, new SimpleBindings ()); //is this code thread-safe?
compiled.eval(new SimpleBindings ()); //and this?
}
catch (Exception e) { throw new RuntimeException (e); }
}
};
threads.start();
}
スレッド間でScriptEngine
およびCompiledScript
オブジェクトを共有できます。それらはスレッドセーフです。単一のエンジンインスタンスはクラスキャッシュとJavaScriptオブジェクトの非表示クラスのホルダーであるため、実際には、それらを共有する必要があります。
共有できないのは、Bindings
オブジェクトです。バインディングオブジェクトは、基本的にJavaScriptランタイム環境のGlobal
オブジェクトに対応します。エンジンはデフォルトのバインディングインスタンスで起動しますが、マルチスレッド環境で使用する場合は、engine.createBindings()
を使用して、スレッドごとに個別のBindingsオブジェクトを取得する必要があります。それ。そうすれば、同じコードで分離されたグローバルスコープを設定できます。 (もちろん、1つのバインディングインスタンスで複数のスレッドが動作しないことを確認するだけで、それらをプールしたり、それらを同期したりすることもできます)。スクリプトをバインディングに評価したら、その後_((JSObject)bindings.get(fnName).call(this, args...)
_で定義された関数を効率的に呼び出すことができます
スレッド間で状態を共有する必要がある場合は、少なくとも変更不可にするようにしてください。オブジェクトが不変の場合、スクリプトを単一のBindings
インスタンスに評価し、それをスレッド間で使用することもできます(できれば副作用のない関数を呼び出します)。可変の場合は、同期する必要があります。バインディング全体、またはvar syncFn = Java.synchronized(fn, lockObj)
Nashorn固有のJS APIを使用して、特定のオブジェクトで同期するJS関数のバージョンを取得することもできます。
これは、スレッド間で単一のバインディングを共有することを前提としています。複数のバインディングでオブジェクトのサブセットを共有する場合(たとえば、同じオブジェクトを複数のバインディングに配置する場合)、共有オブジェクトへのアクセスがスレッドセーフであることを何らかの方法で対処する必要があります。
THREADING
パラメータがnullを返す :はい、最初はエンジンをスレッドセーフにしない(言語自体はスレッドセーフではないと言う)ことを計画していたので、null値を選択しました。その間、エンジンインスタンスがスレッドセーフになるようにしたので、再評価する必要があるかもしれません。JavaScript言語のセマンティクスのため、グローバルスコープ(バインディング)だけではありません(そうなることはありません)。
NashornのScriptEngine
はスレッドセーフではありません。これは、NashornのScriptEngineFactory
のScriptEngineFactory.getParameter("THREADING")
を呼び出すことで確認できます。
返される値はnullです。これは Java doc に従って、スレッドセーフではないことを意味します。
注:答えのこの部分は、最初に here が与えられました。しかし、結果を再確認し、自分で文書化しました。
これにより、CompiledScript
の答えも得られます。 Java doc によると、CompiledScript
は1つのScriptEngine
に関連付けられます。
そのため、NashornではScriptEngine
とCompiledScript
を2つのスレッドで同時に使用しないでください。
受け入れられた答えは多くの人々を誤解させます。
要するに:
NashornScriptEngine
は[〜#〜] not [〜#〜]スレッドセーフです@attillaの応答のコードサンプル
私のjsコードは次のようなものです:
var renderServer = function renderServer(server_data) {
//your js logic...
return html_string.
}
Javaコード:
public static void main(String[] args) {
String jsFilePath = jsFilePath();
String jsonData = jsonData();
try (InputStreamReader isr = new InputStreamReader(new URL(jsFilePath).openStream())) {
NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn");
CompiledScript compiledScript = engine.compile(isr);
Bindings bindings = engine.createBindings();
compiledScript.eval(bindings);
ScriptObjectMirror renderServer = (ScriptObjectMirror) bindings.get("renderServer");
String html = (String) renderServer.call(null, jsonData);
System.out.println(html);
} catch (Exception e) {
e.printStackTrace();
}
}
</ code>
バインディングはスレッドセーフではないため、マルチスレッド環境でrenderServer
メソッドを使用する場合は注意してください。 1つの解決策は、再利用可能なオブジェクトプールでrenderServer
の複数のインスタンスを使用することです。使っています org.Apache.commons.pool2.impl.SoftReferenceObjectPool
、これは私のユースケースではうまく機能しているようです。