Rhino和并发访问javax.script.ScriptEngine

时间:2012-02-04 03:28:32

标签: java javascript rhino

我通过javax.script API使用Rhino 1.6r2。我知道Rhino引擎声称是MULTITHREADED:“引擎实现是内部线程安全的,并且脚本可以并发执行,尽管脚本执行对一个线程的影响可能对其他线程上的脚本可见。”

我想知道的是,在什么条件下,一个脚本执行的效果会对另一个脚本执行的影响?在我的代码中,我有时会重复使用ScriptEngine对象,但是对于每次执行,我都会创建一个新的SimpleBindings并将其传递给eval(String, Bindings)。有了这种安排,内部状态是否有任何方式可以从一个执行泄漏到另一个执行?如果是这样,怎么样?

There's a very informative answer here,但相当告诉我需要了解的内容。

3 个答案:

答案 0 :(得分:4)

javax.script包是线程安全的,但是如果您的脚本不是,那么您可能会遇到并发问题。 脚本中的全局变量对所有线程都是可见的。因此,避免在javascript函数中使用全局变量

我现在正遇到这个问题。我的javascript如下:

function run(){
    regex = 0;
    regex += 1;
    return regex;
}

我在ThreadPool(4)内运行10.000次,并打印结果。

for (int i = 0; i <= 10000; i++){
        executor.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    Double result = (Double) invocable.invokeFunction("run");
                    System.out.println(result);
                } catch (Exception e) {}
            }
        });
    }

这是输出的一部分:

1.0
2.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
1.0
2.0
1.0
1.0
0.0

答案 1 :(得分:3)

是的,JSR223没有指定脚本语言中的变量应该如何与给定的Bindings绑定。因此,实现者完全有可能选择在引擎实例中存储全局范围变量,并在评估脚本时使用不同的Bindings重用它。

例如,JRuby的JSR223绑定有一种模式以这种方式工作

import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

public class Jsr223Binding {


    private Jsr223Binding() throws ScriptException {
        System.setProperty("org.jruby.embed.localvariable.behavior", "transient");
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("jruby");
        ScriptContext ctx1 = new SimpleScriptContext();
        ScriptContext ctx2 = new SimpleScriptContext();
        engine.eval("$foo = 5\nputs $foo", ctx1);
        engine.eval("puts $foo", ctx2);
    }

    public static void main(String[] args) throws ScriptException {
        new Jsr223Binding();
    }
}

答案 2 :(得分:0)

我修改了https://stackoverflow.com/a/1601465/22769的答案,以表明如果在eval()函数中指定上下文,犀牛脚本引擎的执行是completeley线程安全的。该示例同时从5个不同的线程中调用fibonacci javascript函数100次:

package samplethread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;

public class JSRunner {

    private static final ScriptEngine engine;
    private static final ScriptEngineManager manager;

    private static final String script = "function fibonacci(num){\r\n" + 
            "  var a = 1, b = 0, temp;\r\n" + 
            "\r\n" + 
            "  while (num >= 0){\r\n" + 
            "    temp = a;\r\n" + 
            "    a = a + b;\r\n" + 
            "    b = temp;\r\n" + 
            "    num--;\r\n" + 
            "  }\r\n" + 
            "\r\n" + 
            "  return b;\r\n" + 
            "} \r\n" + 
            "var out = java.lang.System.out;\n" + 
            "n = 1;" +
            "while( n <= 100 ) {" +
            "   out.println(java.lang.Thread.currentThread().getName() +':'+ 'FIB('+ n +') = ' + fibonacci(n));" +
            "   n++;" +
            "   if (java.lang.Thread.interrupted()) {" +
            "       out.println('JS: Interrupted::'+Date.now());" +
            "       break;" +
            "   }" +
            "}\n"; 

    static {
        manager = new ScriptEngineManager();
        engine = manager.getEngineByName("JavaScript");
    }

    public static void main(final String... args) throws Exception {
        for(int i = 0;i<5;i++) {
            try {
                final Bindings b = engine.createBindings();
                final SimpleScriptContext sc = new SimpleScriptContext();
                sc.setBindings(b, ScriptContext.ENGINE_SCOPE);
                execWithFuture(engine, script,sc);
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void execWithFuture(final ScriptEngine engine, final String script,final ScriptContext sc) throws Exception {
        System.out.println("Java: Submitting script eval to thread pool...");
        ExecutorService single = Executors.newSingleThreadExecutor();
        Callable<String>  c = new Callable<String>() {

            public String call() throws Exception {
                String result = null;
                try {
                    engine.eval(script,sc);         
                } catch (ScriptException e) {
                    result = e.getMessage();
                } 
                return result;
            }
        };

        single.submit(c);
        single.shutdown();
        System.out.println("Java: ...submitted.");
    }   
}