0

Следует ли использовать отдельные экземпляры ScriptEngine и CompiledScript для каждого потока?

15

Проблема с многопоточностью при использовании Java Scripting API и Nashorn:

Я использую Java Scripting API в своей программе и могу одновременно выполнять некоторые скрипты. Скрипты не используют общие объекты скриптов,Bindings или Context, но могут использовать одни и те же объекты ScriptEngine и CompiledScript. Я заметил, что реализация Nashorn от Oracle в Java 8 не является многопоточной: метод ScriptEngineFactory.getParameter("THREADING") возвращает null, о чем документация говорит следующее:

Реализация движка не является потокобезопасной и не может быть использована для выполнения скриптов одновременно на нескольких потоках.

Возникает вопрос: означает ли это, что мне необходимо создавать отдельный экземпляр ScriptEngine для каждого потока?

Кроме того, в документации ничего не сказано о потокобезопасности CompiledScript, но есть информация, что:

Каждый CompiledScript связан с ScriptEngine.

Можно предположить, что потокобезопасность CompiledScript зависит от связанного с ним ScriptEngine, т.е. я должен использовать отдельный экземпляр CompiledScript для каждого потока при работе с Nashorn.

Если это так, то какое наиболее подходящее решение для этой (как мне кажется, очень распространенной) ситуации? Использовать 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());  // Безопасен ли этот код для потоков?
                compiled.eval(new SimpleBindings());  // А этот?
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    };
    threads.start();
}

Буду признателен за любые советы или рекомендации по решению этой проблемы.

3 ответ(ов)

0

Ответ, который принят, может ввести в заблуждение многих пользователей.

Вкратце:

  • NashornScriptEngine НЕ является потокобезопасным.
  • Если вы НЕ используете глобальные биндинги, то безсостояние (stateless) часть может быть потокобезопасной.
0

Если вы хотите получить код на JavaScript и Java для вызова функции renderServer, а также советы по использованию в многопоточной среде, вот пример, основанный на вашем вопросе.

Пример кода

  1. JavaScript код:
var renderServer = function renderServer(server_data) {
    // Ваша логика на JavaScript...
    return html_string; // здесь должен быть возвращаемый HTML
};
  1. Java код:
public static void main(String[] args) {
    String jsFilePath = jsFilePath(); // Путь к вашему JavaScript файлу
    String jsonData = jsonData(); // Данные в формате JSON

    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();
    }
}

Важно

Обратите внимание, что использование метода renderServer в многопоточной среде требует осторожности, так как привязки (bindings) не являются потокобезопасными. Одним из решений может быть использование нескольких экземпляров renderServer с переиспользуемыми пулами объектов. В моем случае я использую org.apache.commons.pool2.impl.SoftReferenceObjectPool, который, кажется, хорошо работает для моего случая.

Если у вас есть дополнительные вопросы или требуется помощь в дальнейшем, не стесняйтесь спрашивать!

0

Результат моих попыток таков: выполнение одного и того же скрипта в виде строки с использованием ScriptEngine является потокобезопасным, однако проблемы с потокобезопасностью возникают, когда скрипты разные.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь