I'm using Rhino 1.6r2 through the javax.script
API. I know that the Rhino engine claims to be MULTITHREADED: "The engine implementation is internally thread-safe and scripts may execute concurrently although effects of script execution on one thread may be visible to scripts on other threads."
What I'd like to know is, under what exact conditions would the effects of one script execution be visible to another? In my code, I sometimes re-use a ScriptEngine
object, but for every execution I create a new SimpleBindings
and pass it to eval(String, Bindings)
. With this arrangement, is there any way that internal state could leak from one execution to another? If so, how?
There's a very informative answer here, but it doesn't quite tell me what I need to know.
Yes, JSR223 didn't specify how variables in script language should be bound with given Bindings
. Therefore it is totally possible the implementers choose storing global scope variables in engine instance and reuse it even given different Bindings
when evaluating script.
For example, JRuby's JSR223 binding has one mode working in this way
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();
}
}
The javax.script package is thread-safe, but if your script isn't, you can have concurrency problems. The global variables inside the script is visible to all Threads. So, avoid using global variables inside your javascript functions
I'm running into this problem right now. My javascript is as follows:
function run(){
regex = 0;
regex += 1;
return regex;
}
And I'm running it inside a ThreadPool(4) 10.000 times, and printing the result.
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) {}
}
});
}
This is a piece of the output:
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
I modified https://stackoverflow.com/a/1601465/22769 answer to show that rhino script engine execution is completeley thread safe if you specify the context in the eval() function. The sample calls fibonacci javascript function 100 times from 5 different threads concurrently:
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.");
}
}
来源:https://stackoverflow.com/questions/9138159/rhino-and-concurrent-access-to-javax-script-scriptengine