GroovyShell 线程安全

Dan*_*der 1 java groovyshell thread-safety

该问题出现在有关 GroovyShell 的所有问题的评论中,例如使用 GroovyShell 作为“表达式求值器/引擎”(或:如何重用 GroovyShell)。这并不奇怪,因为 API 的设计似乎根本没有涵盖这个主题。不幸的是,这一点从未被明确讨论过。

问题的紧凑形式:

静态初始化:

final GroovyShell shell = new GroovyShell();
final Script thisScript = shell.parse("sleep 1000; return input1+' '+input2");
//anotherScript = // not relevant here but my use-case pre-loads ~300 groovy scripts
Run Code Online (Sandbox Code Playgroud)

脚本运行器:

private Object runScript(Script theScript, String param1, String param2) {
  theScript.setProperty("input1", param1);
  theScript.setProperty("input2", param2);
  Object result = theScript.run();
  return result;
}
Run Code Online (Sandbox Code Playgroud)

序列化执行:

runScript(thisScript, "Hello", "World")   -> Hello World
runScript(thisScript, "Guten", "Tag")     -> Guten Tag
Run Code Online (Sandbox Code Playgroud)

并行执行:

runScript(thisScript, "Hello", "World")   -> Guten Tag (!)
runScript(thisScript, "Guten", "Tag")     -> Guten Tag
Run Code Online (Sandbox Code Playgroud)

问题在于绑定(无论 get/setBinding 还是 setProperty)是在脚本级别完成的。这就像通过 classLoader 加载后在加载的 java.lang.Class 对象上设置某些内容或修改静态成员变量。是否有另一种 gr​​oovy 实现可以将绑定和运行作为原子操作来处理?或者甚至更好:使用上下文对象来执行?

最简单的解决方法是同步runScript()到脚本对象,但这无法扩展。

dag*_*ett 6

创建脚本类的不同实例以并行运行它们。

GroovyShell shell = new GroovyShell();
Class<Script> scriptClass = shell.parse("sleep 1000; return input1+' '+input2").getClass();

Object runScript(Class<Script> clazz, String param1, String param2) {
    Script theScript = clazz.newInstance();
    theScript.setProperty("input1", param1);
    theScript.setProperty("input2", param2);
    Object result = theScript.run();
    return result;
}
//thread test
[
    ["111","aaa"],
    ["222","bbb"]
].collect{x->
    Thread.start{
        println "start $x"
        println runScript(scriptClass, x[0], x[1])
    }
}*.join()
Run Code Online (Sandbox Code Playgroud)

输出:

start [111, aaa]
start [222, bbb]
111 aaa
222 bbb
Run Code Online (Sandbox Code Playgroud)