web-dev-qa-db-ja.com

各スレッドごとに別々のScriptEngineおよびCompiledScriptインスタンスを使用する必要がありますか?

私のプログラムはJava Scripting APIを使用し、いくつかのスクリプトを同時に評価できます。共有スクリプトオブジェクト、バインディング、またはコンテキストは使用しませんが、同じScriptEngineCompiledScriptオブジェクト: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();
}
51
And390

スレッド間で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言語のセマンティクスのため、グローバルスコープ(バインディング)だけではありません(そうなることはありません)。

57
Attila Szegedi

NashornのScriptEngineはスレッドセーフではありません。これは、NashornのScriptEngineFactoryScriptEngineFactory.getParameter("THREADING")を呼び出すことで確認できます。

返される値はnullです。これは Java doc に従って、スレッドセーフではないことを意味します。

注:答えのこの部分は、最初に here が与えられました。しかし、結果を再確認し、自分で文書化しました。

これにより、CompiledScriptの答えも得られます。 Java doc によると、CompiledScriptは1つのScriptEngineに関連付けられます。

そのため、NashornではScriptEngineCompiledScriptを2つのスレッドで同時に使用しないでください

5
Thim Anneessens

受け入れられた答えは多くの人々を誤解させます。

要するに:

  • NashornScriptEngine[〜#〜] not [〜#〜]スレッドセーフです
  • [〜#〜] not [〜#〜]グローバルバインディングを使用する場合、ステートレスパーツ可能スレッドセーフ
0
shawn

@attillaの応答のコードサンプル

  1. 私のjsコードは次のようなものです:

    
    var renderServer = function renderServer(server_data) {
       //your js logic...
       return html_string.
    }
    
  2. 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、これは私のユースケースではうまく機能しているようです。

0
mt.uulu