最近在公司写实时核对系统,其中在核对环节用到了groovy做动态脚本的解析、编译及运算,本文介绍下如何使用能保证线程安全。
引入groovy的相关依赖
<code><dependency>
<groupid>org.codehaus.groovy/<groupid>
<artifactid>groovy/<artifactid>
<version>2.1.6/<version>
/<dependency>
<dependency>
<groupid>org.codehaus.groovy/<groupid>
<artifactid>groovy-jsr223/<artifactid>
<version>2.1.6/<version>
/<dependency>/<code>
因为脚本的编译会花费比较长的时间,不可能在每次请求到达时在编译,所以可以在系统启动的时候先把脚本编译后放入到本地缓存,这里可以使用Map,也可以使用一些其他的框架,比如guava、caffeine都是可以的,后者现在已经是springboot2.x系列首选的本地缓存框架了。
我选择的是使用编译好的Script放在Map里。
在使用的时候有一点需要特别说明,在高并发下会涉及到线程安全的问题。
要保证线程安全,这里有两种做法。
第一种如下:
<code>String expression = = "if (obj.typeId==1) return '1' else return '111111';";
GroovyShell shell = new GroovyShell();
Script/>Binding binding = new Binding();
JSONObject json = new JSONObject();
json.put("typeId", 1);
binding.setVariable("obj", json);
script.setBinding(binding);
Object evaluate =>
这里相当于每次请求进来都会new一个新的对象,线程独享所以线程安全。
第二种方案关键代码如下:
<code>public String equalsCheck() {
Script/>
Binding binding = new Binding();
binding.setProperty("left", checkContext.getReqJson());
binding.setProperty("right", checkContext.getResJson());
/> return InvokerHelper.createScript(script.getClass(), binding).run().toString();
}/<code>
系统启动时先进行预编译,在执行上述方法时先获得在执行。
这种做法是线程安全的,详情如下代码:
代码分析可以看出,方法执行时script的初始值为空,这里的传参class不为空且为Script的class对象,故会都到else的逻辑里。
这里逻辑是根据参数class对象生成一个新的对象,并对变量script进行复制。
如此则每个线程都会有一个单独的Script对象,独享的,binding对象也是该Script对象对象的私有变量,也是线程安全的。
閱讀更多 java知識之路 的文章