java 我应该为每个线程使用单独的 ScriptEngine 和 CompiledScript 实例吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30140103/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Should I use a separate ScriptEngine and CompiledScript instances per each thread?
提问by And390
My program uses Java Scripting API and can eval some scripts concurrently. They don't use shared script objects, Bindings or Context, but can use same ScriptEngine
and CompiledScript
objects. I see that Oracle Nashorn implementation in Java 8 is not multithreaded, ScriptEngineFactory.getParameter('THREADING')
returns null
about which the documentation says:
我的程序使用 Java Scripting API 并且可以同时评估一些脚本。它们不使用共享脚本对象、绑定或上下文,但可以使用相同的ScriptEngine
和CompiledScript
对象。我看到 Java 8 中的 Oracle Nashorn 实现不是多线程的,ScriptEngineFactory.getParameter('THREADING')
返回null
文档说明:
The engine implementation is not thread safe, and cannot be used to execute scripts concurrently on multiple threads.
引擎实现不是线程安全的,不能用于在多个线程上并发执行脚本。
Does it mean that I should create a separate instance of ScriptEngine
for each thread?
Besides, documentation says nothing about CompiledScript
concurrent usage but:
这是否意味着我应该ScriptEngine
为每个线程创建一个单独的实例?此外,文档没有说明CompiledScript
并发使用,但是:
Each CompiledScript is associated with a ScriptEngine
每个 CompiledScript 都与一个 ScriptEngine 相关联
It can be assumed that CompiledScript
thread-safety depends on related ScriptEngine
, i.e. I should use separate CompiledScript
instance for each thread with Nashorn.
可以假设CompiledScript
线程安全取决于相关的ScriptEngine
,即我应该CompiledScript
为 Nashorn 的每个线程使用单独的实例。
If I should, what is the appropriate solution for this (I think very common) case, using ThreadLocal
, a pool or something else?
如果我应该,对于这个(我认为很常见)的情况,使用ThreadLocal
池或其他东西的合适解决方案是什么?
final String script = "...";
final CompiledScript compiled = ((Compilable)scriptEngine).compile(script);
for (int i=0; i<50; i++) {
Thread thread = new Thread () {
public void run() {
try {
scriptEngine.eval(script, new SimpleBindings ()); //is this code thread-safe?
compiled.eval(new SimpleBindings ()); //and this?
}
catch (Exception e) { throw new RuntimeException (e); }
}
};
threads.start();
}
回答by Attila Szegedi
You can share a ScriptEngine
and CompiledScript
objects across threads. They are threadsafe. Actually, you should share them, as a single engine instance is a holder for a class cache and for JavaScript objects' hidden classes, so by having only one you cut down on repeated compilation.
您可以跨线程共享ScriptEngine
和CompiledScript
对象。它们是线程安全的。实际上,您应该共享它们,因为单个引擎实例是类缓存和 JavaScript 对象隐藏类的持有者,因此只有一个引擎实例可以减少重复编译。
What you can't share is Bindings
objects. The bindings object basically corresponds to the JavaScript runtime environment's Global
object. The engine starts with a default bindings instance, but if you use it in multithreaded environment, you need to use engine.createBindings()
to obtain a separate Bindings object for every thread -- its own global, and evaluate the compiled scripts into it. That way you'll set up isolated global scopes with the same code. (Of course, you can also pool them, or synchronize on 'em, just make sure there's never more than one thread working in one bindings instance). Once you evaluated the script into the bindings, you can subsequently efficiently invoke functions it defined with ((JSObject)bindings.get(fnName).call(this, args...)
你不能共享的是Bindings
对象。bindings 对象基本上对应于 JavaScript 运行时环境的Global
对象。引擎以默认绑定实例开始,但是如果在多线程环境中使用它,则需要使用engine.createBindings()
为每个线程获取一个单独的 Bindings 对象——它自己的全局对象,并将编译后的脚本评估到其中。这样,您将使用相同的代码设置隔离的全局作用域。(当然,您也可以将它们池化,或者在它们上同步,只要确保在一个绑定实例中工作的线程永远不会超过一个)。一旦您将脚本评估到绑定中,您就可以随后有效地调用它定义的函数((JSObject)bindings.get(fnName).call(this, args...)
If you must share state across threads, then at least try to make it not mutable. If your objects are immutable, you might as well evaluate the script into single Bindings
instance and then just use that across threads (invoking hopefully side-effect free functions). If it is mutable, you'll have to synchronize; either the whole bindings, or you can also use the var syncFn = Java.synchronized(fn, lockObj)
Nashorn-specific JS API to obtain versions of JS functions that synchronize on a specific object.
如果您必须跨线程共享状态,那么至少尝试使其不可变。如果您的对象是不可变的,您不妨将脚本评估为单个Bindings
实例,然后跨线程使用它(希望调用无副作用的函数)。如果它是可变的,你就必须同步;或者整个绑定,或者您也可以使用var syncFn = Java.synchronized(fn, lockObj)
Nashorn 特定的 JS API 来获取在特定对象上同步的 JS 函数版本。
This presupposes that you share single bindings across threads. If you want to have multiple bindings share a subset of objects (e.g. by putting the same object into multiple bindings), again, you'll have to somehow deal with ensuring that access to shared objects is thread safe yourself.
这假定您跨线程共享单个绑定。如果您想让多个绑定共享对象的子集(例如,将同一个对象放入多个绑定中),同样,您必须以某种方式确保对共享对象的访问是线程安全的。
As for THREADING
parameter returning null: yeah, initially we planned on not making the engine threadsafe (saying that the language itself isn't threadsafe) so we chose the null value. We might need to re-evaluate that now, as in the meantime we did make it so that engine instances are threadsafe, just the global scope (bindings) isn't (and never will be, because of JavaScript language semantics.)
至于THREADING
返回 null 的参数:是的,最初我们计划不使引擎线程安全(说语言本身不是线程安全的),所以我们选择了 null 值。我们现在可能需要重新评估它,因为与此同时我们确实使引擎实例是线程安全的,只是全局范围(绑定)不是(并且永远不会,因为 JavaScript 语言语义。)
回答by Thim Anneessens
ScriptEngine
for Nashorn is not thread-safe. This can be verified by calling the ScriptEngineFactory.getParameter("THREADING")
of the ScriptEngineFactory
for Nashorn.
ScriptEngine
因为 Nashorn 不是线程安全的。这可以通过调用ScriptEngineFactory.getParameter("THREADING")
的ScriptEngineFactory
for Nashorn来验证。
The value returned is null which according to java docmeans not thread safe.
返回的值为 null,根据java doc意味着不是线程安全的。
Note: This part of the answer was first given here. But I rechecked the results and doc myself.
注意:这部分答案首先在这里给出。但我重新检查了结果并自己记录。
This gives us the answer for CompiledScript
as well. According to java doca CompiledScript
is associated to one ScriptEngine
.
这也给了我们答案CompiledScript
。根据java docaCompiledScript
与 one 相关联ScriptEngine
。
So in Nashorn ScriptEngine
and CompiledScript
should not be used by two threads at the same time.
因此,在犀牛ScriptEngine
和CompiledScript
不应由两个线程在同一时间使用。
回答by mt.uulu
Code sample for @attilla's response
@attilla 响应的代码示例
my js code is something like this:
var renderServer = function renderServer(server_data) { //your js logic... return html_string. }
java code:
public static void main(String[] args) { String jsFilePath = jsFilePath(); String jsonData = jsonData();
try (InputStreamReader isr = new InputStreamReader(new URL(jsFilePath).openStream())) { NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); CompiledScript compiledScript = engine.compile(isr); Bindings bindings = engine.createBindings(); compiledScript.eval(bindings); ScriptObjectMirror renderServer = (ScriptObjectMirror) bindings.get("renderServer"); String html = (String) renderServer.call(null, jsonData); System.out.println(html); } catch (Exception e) { e.printStackTrace(); } }
我的js代码是这样的:
var renderServer = function renderServer(server_data) { //your js logic... return html_string. }
代码:
public static void main(String[] args) { String jsFilePath = jsFilePath(); String jsonData = jsonData();
try (InputStreamReader isr = new InputStreamReader(new URL(jsFilePath).openStream())) { NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); CompiledScript compiledScript = engine.compile(isr); Bindings bindings = engine.createBindings(); compiledScript.eval(bindings); ScriptObjectMirror renderServer = (ScriptObjectMirror) bindings.get("renderServer"); String html = (String) renderServer.call(null, jsonData); System.out.println(html); } catch (Exception e) { e.printStackTrace(); } }
be careful when using renderServer
method in multi-threaded environment, since bindings are not thread safe. One solution is to use multiple instances of renderServer
with re-usable object pools. I am using org.apache.commons.pool2.impl.SoftReferenceObjectPool
, which seems to be performing well for my use case.
renderServer
在多线程环境中使用方法时要小心,因为绑定不是线程安全的。一种解决方案是使用renderServer
具有可重用对象池的多个实例。我正在使用org.apache.commons.pool2.impl.SoftReferenceObjectPool
,它似乎对我的用例表现良好。
回答by shawn
The accepted answer will mislead many people.
接受的答案会误导很多人。
In short:
简而言之:
NashornScriptEngine
is NOTthread-safe- If you do NOTuse global bindings, the state-less part can bethread-safe
NashornScriptEngine
是不是线程安全的- 如果您不使用全局绑定,则无状态部分可以是线程安全的