在一次极端的并发测试下,服务器端出现了如下报错:
java.lang.IllegalStateException: Multi threaded access requested by thread Thread[Playertimer-1108213364-Worker-12,5,main] but is not allowed for language(s) js.
根据查询资料显示,GraalJS不允许在多线程下访问脚本,如果出现多线程访问的情况就会出现 如上提示。深一步查询资料,原来是因为GraalJS是不支持同一个Context(上下文)并发访问的,那么解决方案就来了:
1.上悲观锁。
2.对每一条线程都实例化自己的Context,互不干扰。
3.Context池。
很明显,如果使用第一个方案,确实简单粗暴,而且绝对不会再出现这个报错,但是吞吐量就会大大降低,上文我也说过是在极端的并发环境下测试的,那么吞吐量将会是一个很重要的因素。对本人来说,第一个方案不可取。
倘若使用第三个方案,那么问题来了,你首先得设计一个上下文池,然后还得保证每次线程拿到实例绝对不同或者说绝对没有被另外一条线程访问,同时还得考虑到上下文池中的Context对象在同一个时间片耗尽的问题。这样复杂度大大提升了,虽然可以解决问题,但是很明显代价太大了,所以对本人来说第三个方案也不可取。
那么,就只剩第二种方案了。当然,这是对我查到的方案来说,可能会存在更好的解决方案。
第二个方案很简单,字面意思,每个线程都实例化自己的Context实例,互不干扰,我找到了如下一段代码:
[mw_shl_code=java,true] Engine tmpEngine = Engine
.newBuilder()
.allowExperimentalOptions(true)
.build();
final ThreadLocal<Context> tl = ThreadLocal.withInitial(() -> Context
.newBuilder("js")
.allowExperimentalOptions(true)
.engine(tmpEngine)
.build());
tl.get().eval("js", "console.log('asd')");[/mw_shl_code]
这段代码也非常的简明:首先实例化引擎,然后用ThreadLocal对象让每个线程实例化自己的Context对象。
我是用线程池测试了一下并发的情况,在一秒后执行1000次“运行JS代码”的任务:
结果很明显,成功了,再也没有提示IllegalStateException。
不过,这种方式对于我的代码并不适用,我需要对它进行改动,首先简洁说明一下我的代码逻辑:使用ScriptEngineManager实例创建scriptEngineFactory工厂实例,然后使用scriptEngineFactory创建GraalJSScriptEngine,最后通过GraalJSScriptEngine实例调用JS代码,示例代码如下(注意示例代码中的threadPool对象是我自定义的线程池对象):[mw_shl_code=java,true] ScriptEngineFactory scriptEngineFactory = new ScriptEngineManager().getEngineByName("graal.js").getFactory();
final ThreadLocal<GraalJSScriptEngine> tl = ThreadLocal.withInitial(() -> {
GraalJSScriptEngine scriptEngine = (GraalJSScriptEngine) scriptEngineFactory.getScriptEngine();
try {
scriptEngine.eval("load('nashorn:mozilla_compat.js');" + System.lineSeparator());
scriptEngine.eval("function setup() { console.log('abc'); }");
} catch (ScriptException e) {
e.printStackTrace();
}
return scriptEngine;
});
for (int i = 0; i < 1000; i++) {
threadPool.schedule(() -> {
try {
tl.get().invokeFunction("setup", (Object) null);
} catch (ScriptException | NoSuchMethodException e) {
e.printStackTrace();
}
}, 1000L);
}
[/mw_shl_code]
运行结果:
大功告成!
Felix 2022年5月19日 11:39 |
|