java - temporizador - ¿Debo usar una instancia separada de ScriptEngine y CompiledScript por cada hilo?
reloj java hilos (4)
Ejemplo de código para la respuesta de @ attilla
-
mi código js es algo como esto:
var renderServer = function renderServer(server_data) { //your js logic... return html_string. }
-
código java:
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(); } }
tenga cuidado al usar el método
renderServer
en un entorno de subprocesos múltiples, ya que los enlaces no son seguros para subprocesos.
Una solución es usar varias instancias de
renderServer
con
renderServer
de
renderServer
reutilizables.
Estoy usando
org.apache.commons.pool2.impl.SoftReferenceObjectPool
, que parece estar funcionando bien para mi caso de uso.
Mi programa utiliza la API de Java Scripting y puede evaluar algunos scripts simultáneamente.
No usan objetos de script compartidos, enlaces o contexto, pero pueden usar los mismos objetos
ScriptEngine
y
CompiledScript
.
Veo que la implementación de Oracle Nashorn en Java 8 no es multiproceso,
ScriptEngineFactory.getParameter(''THREADING'')
devuelve un
null
sobre el que dice la documentación:
La implementación del motor no es segura para subprocesos y no se puede usar para ejecutar scripts simultáneamente en múltiples subprocesos.
¿Significa que debería crear una instancia separada de
ScriptEngine
para cada hilo?
Además, la documentación no dice nada sobre el uso concurrente de
CompiledScript
pero:
Cada CompiledScript está asociado con un ScriptEngine
Se puede suponer que la seguridad de subprocesos de
CompiledScript
depende de
ScriptEngine
relacionado, es decir, debería usar una instancia de
CompiledScript
separada para cada subproceso con Nashorn.
Si debo, ¿cuál es la solución adecuada para este caso (creo que es muy común), usando
ThreadLocal
, un grupo u otra cosa?
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();
}
La respuesta aceptada engañará a muchas personas.
En breve:
-
NashornScriptEngine
NO es seguro para subprocesos - Si NO utiliza enlaces globales, la parte sin estado puede ser segura para subprocesos
Puede compartir un
ScriptEngine
y objetos
CompiledScript
entre hilos.
Son a prueba de hilos.
En realidad, debe compartirlos, ya que una instancia de un solo motor es un soporte para un caché de clase y para las clases ocultas de los objetos JavaScript, por lo que al tener solo uno reduce la compilación repetida.
Lo que no puedes compartir son los objetos de
Bindings
.
El objeto de enlaces corresponde básicamente al objeto
Global
del entorno de tiempo de ejecución de JavaScript.
El motor comienza con una instancia de enlaces predeterminada, pero si lo usa en un entorno multiproceso, debe usar
engine.createBindings()
para obtener un objeto de enlaces separado para cada hilo, su propio global, y evaluar las secuencias de comandos compiladas en él.
De esa manera, configurará ámbitos globales aislados con el mismo código.
(Por supuesto, también puede agruparlos o sincronizarlos, solo asegúrese de que nunca haya más de un hilo trabajando en una instancia de enlaces).
Una vez que evaluó la secuencia de comandos en los enlaces, puede invocar eficientemente las funciones que definió con
((JSObject)bindings.get(fnName).call(this, args...)
Si debe compartir el estado entre subprocesos, al menos intente que no sea mutable.
Si sus objetos son inmutables, también podría evaluar el script en una única instancia de
Bindings
y luego usarlo en todos los hilos (invocando funciones libres de efectos secundarios).
Si es mutable, tendrá que sincronizar;
ya sea los enlaces completos, o también puede usar
var syncFn = Java.synchronized(fn, lockObj)
API JS específica de Nashorn para obtener versiones de funciones JS que se sincronizan en un objeto específico.
Esto presupone que comparte enlaces individuales entre hilos. Si desea que varios enlaces compartan un subconjunto de objetos (por ejemplo, al poner el mismo objeto en múltiples enlaces), nuevamente, tendrá que lidiar de alguna manera para garantizar que el acceso a los objetos compartidos sea seguro para los hilos.
En cuanto al
parámetro
THREADING
que devuelve nulo
: sí, inicialmente planeamos no hacer que el motor fuera seguro (diciendo que el lenguaje en sí mismo no es seguro), por lo que elegimos el valor nulo.
Es posible que necesitemos reevaluar eso ahora, ya que mientras tanto lo hicimos para que las instancias del motor sean seguras para subprocesos, solo el alcance global (enlaces) no es (y nunca lo será, debido a la semántica del lenguaje JavaScript).
ScriptEngine
para Nashorn no es
ScriptEngine
para subprocesos.
Esto se puede verificar llamando a
ScriptEngineFactory.getParameter("THREADING")
de
ScriptEngineFactory
para Nashorn.
El valor devuelto es nulo, lo que según java doc significa que no es seguro para subprocesos.
Nota: Esta parte de la respuesta se dio primero here . Pero volví a comprobar los resultados y a mí mismo.
Esto también nos da la respuesta para
CompiledScript
.
Según
java doc,
un
CompiledScript
está asociado a un
ScriptEngine
.
Por lo tanto, en Nashorn
ScriptEngine
y
CompiledScript
no deben ser utilizados por dos hilos al mismo tiempo
.