¿Debo usar instancias separadas de ScriptEngine y CompiledScript por cada hilo?

Mi progtwig usa Java Scripting API y puede evaluar algunos scripts al mismo tiempo. No utilizan 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 lo que dice la documentación:

La implementación del motor no es segura para subprocesos y no se puede usar para ejecutar secuencias de comandos simultáneamente en varios 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 sí:

Cada CompiledScript está asociado con un ScriptEngine

Se puede suponer que CompiledScript thread-safety depende de ScriptEngine relacionado, es decir, debería usar una instancia de CompiledScript separada para cada hilo con Nashorn.

Si debería, ¿cuál es la solución adecuada para este caso (creo que es muy común), utilizando ThreadLocal , un grupo o algo más?

  final String script = "..."; final CompiledScript compiled = ((Comstackble)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(); } 

Puede compartir un ScriptEngine y objetos CompiledScript través de hilos. Ellos son seguros para los hilos En realidad, debe compartirlos, ya que una instancia de un solo motor es un soporte para un caché de clase y para clases ocultas de objetos de JavaScript, de modo que al tener solo uno se reduce la comstackción repetida.

Lo que no puedes compartir es objetos de Bindings . El objeto de enlaces corresponde básicamente al objeto Global del entorno de ejecución de JavaScript. El motor comienza con una instancia de enlaces predeterminada, pero si la usa en un entorno multiproceso, debe usar engine.createBindings() para obtener un objeto Bindings separado para cada hilo, su propio global, y evaluar los scripts comstackdos en él. De esta forma, configurará ámbitos globales aislados con el mismo código. (Por supuesto, también puedes agruparlos o sincronizarlos, solo asegúrate de que nunca haya más de un hilo trabajando en una instancia de enlaces). Una vez que haya evaluado el guión en los enlaces, puede invocar funciones definidas 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 usar eso en los hilos (invocando con suerte funciones libres de efectos secundarios). Si es mutable, tendrás que sincronizar; cualquiera de los enlaces enteros, 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 comparta enlaces únicos entre subprocesos. Si desea que varios enlaces compartan un subconjunto de objetos (por ejemplo, colocando el mismo objeto en enlaces múltiples), de nuevo tendrá que ocuparse de alguna manera de garantizar que el acceso a los objetos compartidos sea seguro para los hilos.

En cuanto al parámetro THREADING que devuelve null : sí, al principio planeamos no hacer que el motor sea seguro para el hilo (diciendo que el lenguaje en sí no es threadsafe) así que elegimos el valor nulo. Es posible que tengamos que volver a evaluar eso ahora, mientras tanto lo hicimos para que las instancias del motor sean seguras para el hilo, solo el scope global (enlaces) no es (y nunca lo será, debido a la semántica del lenguaje JavaScript).

ScriptEngine for Nashorn no es seguro para subprocesos. Esto se puede verificar llamando a ScriptEngineFactory.getParameter (“THREADING”) de ScriptEngineFactory para Nashorn.

El valor devuelto es nulo, lo que de acuerdo con el documento de Java significa que no es seguro para subprocesos.

Nota: Esta parte de la respuesta se dio primero aquí . Pero volví a comprobar los resultados y doc.

Esto nos da la respuesta para CompiledScript también. De acuerdo con Java Doc, CompiledScript está asociado a un ScriptEngine.

Por lo tanto, en Nashorn ScriptEngine y CompiledScript no deben ser utilizados por dos hilos al mismo tiempo .

Ejemplo de código para la respuesta de @ attilla

  1. mi código js es algo como esto:

     var renderServer = function renderServer(server_data) { //your js logic... return html_string. } 
  2. 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 entornos 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 funcionar bien para mi caso de uso.

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