我通过javax.script
API使用Rhino 1.6r2。我知道Rhino引擎声称是MULTITHREADED:“引擎实现是内部线程安全的,并且脚本可以并发执行,尽管脚本执行对一个线程的影响可能对其他线程上的脚本可见。”
我想知道的是,在什么条件下,一个脚本执行的效果会对另一个脚本执行的影响?在我的代码中,我有时会重复使用ScriptEngine
对象,但是对于每次执行,我都会创建一个新的SimpleBindings
并将其传递给eval(String, Bindings)
。有了这种安排,内部状态是否有任何方式可以从一个执行泄漏到另一个执行?如果是这样,怎么样?
There's a very informative answer here,但相当告诉我需要了解的内容。
答案 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.");
}
}