Nashorn in thread safe manner

2019-05-26 04:35发布

问题:

Below I have shared my code in which I am trying to use thread safe Nashorn as scripting engine to evaluate simple mathematical formula. Formula will be like "a*b/2" where a & b will be picked up from map object.

public class EvaluateFormulas {

@Autowired
NashornThreadPool pool;

private ScriptEngineManager factory;
private ScriptEngine engine;
private ScriptContext context;

@PostConstruct
public void setup() {
    factory = new ScriptEngineManager();
    engine = factory.getEngineByName("nashorn");
    context = engine.getContext();
}

public void evaluate() {
    pool.getThreadPoolExecutor().submit(new Runnable() {
        @Override
        public void run() {
            Double result;
            Bindings bind = engine.createBindings();
            bind.putAll(map);
            context.setBindings(bind, ScriptContext.GLOBAL_SCOPE);
            try {
                result = (Double) engine.eval(formula);
            } catch (ScriptException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}   
}

I need to know whether this approach will help making Nashorn thread safe for this use case. I read in Attila`s answer that we can share script engine objects between threads since they are thread safe.

For bindings and eval, since we are creating new thread for each execution of evaluate and each thread has its own object reference for bindings object. So in its entirety, will this implementation be thread safe?

回答1:

I don't think this will be threadsafe, since you're changing the bindings while another thread could potentially be executing code. You actually don't need to be doing this; you can just create a brand-new context and then evaluate the script in that context. I would do something like this:

public void evaluate() {
    pool.getThreadPoolExecutor().submit(new Runnable() {
        @Override
        public void run() {
            Double result;

            ScriptContext myContext = new SimpleScriptContext();
            Bindings bindings = engine.createBindings();
            bindings.putAll(map);
            myContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);

            try {
                result = (Double) engine.eval(formula, myContext);
            } catch (ScriptException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

Now each script gets evaluated in its own context.