GroovyShell线程安全

问题描述

该问题出现在有关GroovyShell的所有问题的注释中,例如Using GroovyShell as "expression evaluator/engine" (or: How to reuse 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

脚本运行器:

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

序列化执行:

runScript(thisScript,"Hello","World")   -> Hello World
runScript(thisScript,"Guten","Tag")     -> Guten Tag

并行执行:

runScript(thisScript,"World")   -> Guten Tag (!)
runScript(thisScript,"Tag")     -> Guten Tag

问题在于绑定(无论是get / setBinding还是setProperty)都在脚本级别完成。这就像通过classLoader加载了java.lang.Class对象或修改静态成员变量之后,对其进行了设置。是否有替代的常规实现将绑定和运行作为原子操作来处理?甚至更好:使用上下文对象执行?

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

解决方法

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

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()

输出:

start [111,aaa]
start [222,bbb]
111 aaa
222 bbb

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...