Rhino和并发访问javax.script.ScriptEngine

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

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

这里有一个非常丰富的答案 ,但它并不能告诉我我需要知道什么。

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

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 

我修改了https://stackoverflow.com/a/1601465/22769答案,表明如果在eval()函数中指定上下文,则rhino脚本引擎执行完全是线程安全的。 该示例同时从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 c = new Callable() { 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."); } }