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

标签 java multithreading nashorn jsr223

我的程序使用Java Scripting API,可以同时评估一些脚本。它们不使用共享脚本对象、绑定(bind)或上下文,但可以使用相同的 ScriptEngine 和 CompiledScript 对象。我发现 Java 8 中的 Oracle Nashorn 实现不是多线程的,ScriptEngineFactory.getParameter('THREADING') 返回 null,文档对此表示:

The engine implementation is not thread safe, and cannot be used to execute scripts concurrently on multiple threads.

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

Each CompiledScript is associated with a 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();
}

最佳答案

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

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

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

这假定您跨线程共享单个绑定(bind)。如果您想让多个绑定(bind)共享对象的子集(例如,通过将同一对象放入多个绑定(bind)中),那么您必须以某种方式确保对共享对象的访问是线程安全的。

至于THREADING parameter returning null :是的,最初我们计划不使引擎线程安全(也就是说语言本身不是线程安全的),所以我们选择了 null 值。我们现在可能需要重新评估这一点,因为与此同时我们确实使引擎实例是线程安全的,只是全局范围(绑定(bind))不是(并且永远不会,因为 JavaScript 语言语义)。

关于java - 我应该为每个线程使用单独的 ScriptEngine 和 CompiledScript 实例吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47544932/

相关文章:

java - 在线程执行 'A'时是否可以阻止对 'B'的线程访问? [复制]

c - 线程创建将在一段时间后停止工作

java - 是否可以让 Nashorn 从类路径加载脚本?

java - Nashorn 脚本引擎在 Java 8 SE 嵌入式上为空

java - Libgdx 帧缓冲区混合不起作用

java.lang.NullPointerException 错误请

c++ - 使用 futures 在 C++ 中并行创建直方图 : how to use a template function with future?

java - 按钮布局问题 - JFrame

java - 一旦 map 被 Hazelcast 填充,就从 map 中获取一个值

java - 解释时获取 Nashorn 脚本行号