Dois-je utiliser des instances ScriptEngine et ComstackdScript distinctes pour chaque thread?

Mon programme utilise l’API Java Scripting et peut évaluer certains scripts simultanément. Ils n’utilisent pas les objects de script partagés, Bindings ou Context, mais peuvent utiliser les mêmes objects ScriptEngine et ComstackdScript . Je vois que l’implémentation d’Oracle Nashorn dans Java 8 n’est pas multithreadée, ScriptEngineFactory.getParameter('THREADING') renvoie la valeur null propos de laquelle la documentation dit:

L’implémentation du moteur n’est pas thread-safe et ne peut pas être utilisée pour exécuter des scripts simultanément sur plusieurs threads.

Est-ce que cela signifie que je devrais créer une instance distincte de ScriptEngine pour chaque thread? De plus, la documentation ne dit rien sur l’utilisation concurrente de ComstackdScript mais:

Chaque ComstackdScript est associé à un ScriptEngine

On peut supposer que la sécurité des threads de ComstackdScript dépend de ScriptEngine , c’est-à-dire que je devrais utiliser une instance ComstackdScript distincte pour chaque thread avec Nashorn.

Si je devais, quelle est la solution appropriée pour ce cas (je pense très commun), en utilisant ThreadLocal , un pool ou autre chose?

  final Ssortingng script = "..."; final ComstackdScript comstackd = ((Compilable)scriptEngine).comstack(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(); } 

Vous pouvez partager des objects ScriptEngine et ComstackdScript sur les threads. Ils sont threadsafe. En fait, vous devriez les partager, car une instance de moteur unique est un support pour un cache de classe et pour les classes cachées des objects JavaScript, donc en ayant un seul, vous réduisez la compilation répétée.

Ce que vous ne pouvez pas partager, ce sont les objects Bindings . L’object bindings correspond essentiellement à l’object Global l’environnement d’exécution JavaScript. Le moteur démarre avec une instance de liaisons par défaut, mais si vous l’utilisez dans un environnement multithread, vous devez utiliser engine.createBindings() pour obtenir un object Bindings distinct pour chaque thread – son propre global et y évaluer les scripts compilés. De cette façon, vous allez configurer des étendues globales isolées avec le même code. (Bien sûr, vous pouvez également les regrouper ou les synchroniser, assurez-vous simplement qu’il n’y a jamais plus d’un thread travaillant dans une instance de liaison). Une fois que vous avez évalué le script dans les liaisons, vous pouvez ensuite invoquer efficacement les fonctions définies avec ((JSObject)bindings.get(fnName).call(this, args...)

Si vous devez partager un état entre les threads, essayez au moins de le rendre non mutable. Si vos objects sont immuables, vous pourriez aussi bien évaluer le script en une seule instance de Bindings , puis simplement l’utiliser à travers les threads (en appelant des fonctions libres d’effets secondaires). S’il est mutable, vous devrez effectuer une synchronisation. soit les liaisons entières, soit vous pouvez également utiliser l’API JS var syncFn = Java.synchronized(fn, lockObj) pour obtenir les versions des fonctions JS qui se synchronisent sur un object spécifique.

Cela suppose que vous partagiez des liaisons uniques entre les threads. Si vous souhaitez que plusieurs liaisons partagent un sous-ensemble d’objects (en plaçant par exemple le même object dans plusieurs liaisons), vous devrez à nouveau veiller à ce que l’access aux objects partagés soit vous-même sécurisé.

En ce qui concerne le paramètre THREADING renvoyant null : oui, initialement, nous avions prévu de ne pas rendre le moteur threadsafe (en disant que le langage lui-même n’est pas threadsafe), nous avons donc choisi la valeur null. Nous devrons peut-être réévaluer cela maintenant, car entre temps nous avons fait en sorte que les instances de moteur soient threadsafe, juste la scope globale (bindings) n’est pas (et ne le sera jamais, à cause de la sémantique du langage JavaScript).

ScriptEngine for Nashorn n’est pas compatible avec les threads. Cela peut être vérifié en appelant ScriptEngineFactory.getParameter (“THREADING”) de ScriptEngineFactory pour Nashorn.

La valeur renvoyée est null, ce qui, selon doc java , ne signifie pas thread-safe.

Note: Cette partie de la réponse a été donnée ici . Mais j’ai revérifié les résultats et doc moi-même.

Cela nous donne également la réponse pour ComstackdScript. Selon java doc, un ComstackdScript est associé à un ScriptEngine.

Ainsi, dans Nashorn, ScriptEngine et ComstackdScript ne doivent pas être utilisés par deux threads simultanément .

Exemple de code pour la réponse de @attilla

  1. mon code js est quelque chose comme ça:

     var renderServer = function renderServer(server_data) { //your js logic... return html_ssortingng. } 
  2. code java:

     public static void main(Ssortingng[] args) { Ssortingng jsFilePath = jsFilePath(); Ssortingng jsonData = jsonData(); 
      try (InputStreamReader isr = new InputStreamReader(new URL(jsFilePath).openStream())) { NashornScriptEngine engine = (NashornScriptEngine) new ScriptEngineManager().getEngineByName("nashorn"); ComstackdScript comstackdScript = engine.comstack(isr); Bindings bindings = engine.createBindings(); comstackdScript.eval(bindings); ScriptObjectMirror renderServer = (ScriptObjectMirror) bindings.get("renderServer"); Ssortingng html = (Ssortingng) renderServer.call(null, jsonData); System.out.println(html); } catch (Exception e) { e.printStackTrace(); } } 

Soyez prudent lorsque vous renderServer méthode renderServer dans un environnement multi-thread, car les liaisons ne sont pas thread-safe. Une solution consiste à utiliser plusieurs instances de renderServer avec des pools d’objects réutilisables. J’utilise org.apache.commons.pool2.impl.SoftReferenceObjectPool , qui semble bien fonctionner pour mon cas d’utilisation.

La réponse acceptée induira beaucoup de gens en erreur.

En bref:

  • NashornScriptEngine N’EST PAS NashornScriptEngine threads
  • Si vous n’utilisez PAS de liaisons globales, la partie sans état peut être thread-safe