一尘不染

是否应该在每个线程中使用单独的ScriptEngine和CompiledScript实例?

java

我的程序使用Java Scripting
API,并且可以同时评估某些脚本。他们不使用共享脚本对象,绑定或上下文,但可以使用ScriptEngineCompiledScript对象。我看到,在Java的甲骨文8实施犀牛不是多线程的,ScriptEngineFactory.getParameter('THREADING')回报率null约该文件说:

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

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

每个CompiledScript与一个ScriptEngine相关联

可以假设CompiledScript线程安全性取决于related
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();
}

阅读 517

收藏
2020-09-08

共1个答案

一尘不染

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

您无法共享的是Bindings对象。绑定对象基本上对应于JavaScript运行时环境的Global对象。该引擎以默认的绑定实例开始,但是如果在多线程环境中使用它,则需要使用它engine.createBindings()为每个线程(它自己的全局变量)获取一个单独的Bindings对象,并评估其中的编译脚本。这样,您将使用相同的代码设置隔离的全局范围。(当然,您也可以合并它们,或在’em上进行同步,只需确保在一个绑定实例中工作的线程不超过一个)。将脚本评估为绑定之后,您可以随后有效地调用其定义的函数((JSObject)bindings.get(fnName).call(this, args...)

如果必须在线程之间共享状态,则至少尝试使其不可变。如果您的对象是不可变的,则最好将脚本评估为单个Bindings实例,然后跨线程使用它(希望调用无副作用的函数)。如果是可变的,则必须进行同步;既可以是整个绑定,也可以使用var syncFn = Java.synchronized(fn, lockObj)Nashorn特定的JS API来获取在特定对象上同步的JS函数的版本。

前提是您在线程之间共享单个绑定。如果要让多个绑定共享对象的子集(例如,通过将同一个对象放入多个绑定中),则必须以某种方式处理确保对共享对象的访问本身是线程安全的。

至于THREADING返回null的参数:是的,最初我们计划不使引擎具有线程安全性(假设语言本身不是线程安全的),所以我们选择了null值。我们现在可能需要重新评估,因为与此同时我们做到了,以便引擎实例是线程安全的,只是全局范围(绑定)不是(并且永远不会,因为JavaScript语言语义)。

2020-09-08