Should I use a separate ScriptEngine and CompiledScript instances per each thread?

强颜欢笑 提交于 2019-11-26 17:22:43
Attila Szegedi

You can share a ScriptEngine and CompiledScript objects across threads. They are threadsafe. Actually, you should share them, as a single engine instance is a holder for a class cache and for JavaScript objects' hidden classes, so by having only one you cut down on repeated compilation.

What you can't share is Bindings objects. The bindings object basically corresponds to the JavaScript runtime environment's Global object. The engine starts with a default bindings instance, but if you use it in multithreaded environment, you need to use engine.createBindings() to obtain a separate Bindings object for every thread -- its own global, and evaluate the compiled scripts into it. That way you'll set up isolated global scopes with the same code. (Of course, you can also pool them, or synchronize on 'em, just make sure there's never more than one thread working in one bindings instance). Once you evaluated the script into the bindings, you can subsequently efficiently invoke functions it defined with ((JSObject)bindings.get(fnName).call(this, args...)

If you must share state across threads, then at least try to make it not mutable. If your objects are immutable, you might as well evaluate the script into single Bindings instance and then just use that across threads (invoking hopefully side-effect free functions). If it is mutable, you'll have to synchronize; either the whole bindings, or you can also use the var syncFn = Java.synchronized(fn, lockObj) Nashorn-specific JS API to obtain versions of JS functions that synchronize on a specific object.

This presupposes that you share single bindings across threads. If you want to have multiple bindings share a subset of objects (e.g. by putting the same object into multiple bindings), again, you'll have to somehow deal with ensuring that access to shared objects is thread safe yourself.

As for THREADING parameter returning null: yeah, initially we planned on not making the engine threadsafe (saying that the language itself isn't threadsafe) so we chose the null value. We might need to re-evaluate that now, as in the meantime we did make it so that engine instances are threadsafe, just the global scope (bindings) isn't (and never will be, because of JavaScript language semantics.)

Thim Anneessens

ScriptEngine for Nashorn is not thread-safe. This can be verified by calling the ScriptEngineFactory.getParameter("THREADING") of the ScriptEngineFactory for Nashorn.

The value returned is null which according to java doc means not thread safe.

Note: This part of the answer was first given here. But I rechecked the results and doc myself.

This gives us the answer for CompiledScript as well. According to java doc a CompiledScript is associated to one ScriptEngine.

So in Nashorn ScriptEngine and CompiledScript should not be used by two threads at the same time.

Code sample for @attilla's response

  1. my js code is something like this:

    
    var renderServer = function renderServer(server_data) {
       //your js logic...
       return html_string.
    }
    
  2. java code:

    
    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();
       }
    }
    

be careful when using renderServer method in multi-threaded environment, since bindings are not thread safe. One solution is to use multiple instances of renderServer with re-usable object pools. I am using org.apache.commons.pool2.impl.SoftReferenceObjectPool, which seems to be performing well for my use case.

shawn

The accepted answer will mislead many people.

In short:

  • NashornScriptEngine is NOT thread-safe
  • If you do NOT use global bindings, the state-less part can be thread-safe
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!