我应该为每个线程使用单独的ScriptEngine和CompiledScript实例吗?

And*_*390 52 java multithreading jsr223 nashorn

我的程序使用Java Scripting API并且可以同时评估一些脚本.他们不使用共享脚本对象,绑定或上下文,但可以使用相同ScriptEngineCompiledScript对象.我看到Java 8中的Oracle Nashorn实现不是多线程的,ScriptEngineFactory.getParameter('THREADING')返回null文档所说的内容:

引擎实现不是线程安全的,不能用于在多个线程上并发执行脚本.

这是否意味着我应该ScriptEngine为每个线程创建一个单独的实例?此外,文档没有说明CompiledScript并发使用,但:

每个CompiledScript都与ScriptEngine相关联

可以假设CompiledScript线程安全依赖于相关ScriptEngine,即我应该CompiledScript为Nashorn的每个线程使用单独的实例.

如果我应该,对于这个(我认为非常常见的)案例,使用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();
}
Run Code Online (Sandbox Code Playgroud)

Att*_*edi 58

您可以跨线程共享一个ScriptEngineCompiledScript对象.它们是线程安全的.实际上,您应该共享它们,因为单个引擎实例是类缓存和JavaScript对象的隐藏类的持有者,因此只有一个可以减少重复编译.

你不能分享的是Bindings对象.绑定对象基本上对应于JavaScript运行时环境的Global对象.引擎以默认绑定实例启动,但如果在多线程环境中使用它,则需要使用engine.createBindings()为每个线程获取单独的Bindings对象 - 它自己的全局,并将编译后的脚本评估到其中.这样,您将使用相同的代码设置隔离的全局范围.(当然,您也可以对它们进行池化,或者在它们上进行同步,只需确保在一个绑定实例中永远不会有多个线程工作).将脚本评估为绑定后,您可以随后有效地调用它定义的函数((JSObject)bindings.get(fnName).call(this, args...)

如果您必须跨线程共享状态,那么至少尝试使其不可变.如果你的对象是不可变的,你也可以将脚本评估为单个Bindings实例,然后只在线程中使用它(调用有希望的副作用自由函数).如果它是可变的,你将不得不同步; 要么是整个绑定,要么也可以使用var syncFn = Java.synchronized(fn, lockObj)Nashorn特定的JS API来获取在特定对象上同步的JS函数版本.

这预示着您在线程之间共享单个绑定.如果你想让多个绑定共享一个对象的子集(例如,通过将同一个对象放入多个绑定中),那么你将不得不以某种方式处理确保自己对共享对象的访问是线程安全的.

至于THREADING参数返回null:是的,最初我们计划不使引擎线程安全(说语言本身不是线程安全的),所以我们选择了null值.我们现在可能需要重新评估,因为在此期间我们确实使得引擎实例是线程安全的,只是全局范围(绑定)不是(并且永远不会,因为JavaScript语言语义.)

  • 使用One JSEngine为所有线程提供服务时遇到严重的串扰问题.虽然在每次执行之前都复制了绑定(使用putAll),但JMeter测试对整体结果来说是致命的.结束了做threadlocal引擎. (3认同)
  • 是否有任何代码示例显示如何在多线程环境中正确使用Nashorn?另外,我在`ScriptEngine`上找不到`newBindings()`方法 - 它应该是`createBindings()`吗? (2认同)

小智 5

ScriptEngineNashorn不是线程安全的.这可以通过调用验证ScriptEngineFactory.getParameter("THREADING")ScriptEngineFactory的犀牛.

返回的值为null,根据java doc表示不是线程安全的.

注意:这部分答案首先在这里给出.但我自己重新检查结果和文档.

这也为我们提供了答案CompiledScript.根据java doc, a CompiledScript与一个相关联ScriptEngine.

因此,在犀牛ScriptEngineCompiledScript不应由两个线程在同一时间使用.

  • 这与阿提拉更全面的答案相矛盾.鉴于他的背景和提供的背景,我认为这是正确的. (11认同)