У меня есть сценарий, когда я обрабатываю предоставленные пользователем шаблоны, которые содержат части JavaScript.Я использую Java ScriptEngine
для оценки этих кусочков JavaScript.У меня есть различные вспомогательные функции, которые я хочу оценивать только один раз (представьте, что эти вспомогательные функции загружают что-то вроде Underscore.js
).
После загрузки я перебираю строки данных и обрабатываю шаблон длякаждая строка - эти шаблоны могут использовать любую из ранее загруженных вспомогательных функций.Я пытаюсь обрабатывать строки параллельно, поскольку ScriptEngine.eval()
в Java в этом сценарии довольно медленный.
Любой, кто смотрит на это по упрощенному SSCCE, будет знать, что я получаю ожидаемый результат, который я показал, так какЯ изменяю глобальный объект не потокобезопасным способом.В моем примере sJsGlobal
это глобальный JavaScript.Глобальные функции нуждаются в доступе к различным битам, специфичным для строк, которые я помещаю в obj
.
Для sJsGlobal
для успешного eval()
необходимо знать о obj
, иначе произойдет сбой.В моем SSCCE каждый поток обновляет один и тот же глобальный obj
перед вызовом функции.Я ищу способ для каждой глобальной функции видеть локальный obj
.Это ситуация Catch-22, мне нужно определить переменную для глобального сценария, чтобы он знал, что он существует, но я хочу, чтобы он имел локальное значение.
import java.util.*;
import java.util.concurrent.*;
import javax.script.*;
public class SSCCE {
public static void main(String[] args) throws Exception {
StringBuilder sJsGlobal = new StringBuilder();
sJsGlobal.append("var obj = {};");
sJsGlobal.append("function get_row() { return \"Row is \" + obj.row }");
ScriptEngine jsGlobal = new ScriptEngineManager().getEngineByName("JavaScript");
jsGlobal.eval(sJsGlobal.toString());
Bindings gB = jsGlobal.getBindings(ScriptContext.ENGINE_SCOPE);
ExecutorService es = Executors.newCachedThreadPool();
ArrayList<Future<String>> af = new ArrayList<Future<String>>();
for (int i = 0; i < 10; i++) {
final int fi = i;
af.add(es.submit(() -> {
StringBuilder sJsLocal = new StringBuilder();
sJsLocal.append("obj.row = " + fi + ";");
sJsLocal.append("var x = get_row();");
ScriptEngine jsLocal = new ScriptEngineManager().getEngineByName("JavaScript");
ScriptContext sc = new SimpleScriptContext();
Bindings lB = jsLocal.createBindings();
lB.putAll(gB);
sc.setBindings(lB, ScriptContext.ENGINE_SCOPE);
jsLocal.setContext(sc);
jsLocal.eval(sJsLocal.toString());
return (String)jsLocal.get("x");
}));
}
for (Future<String> f : af) {
System.out.println("Returned -> " + f.get());
}
es.shutdown();
}
}
Это приводит к следующему выводу, гдестроки перезаписываются, так как они не являются потокобезопасными из-за моего использования этой глобальной переменной obj
.Даже с отдельными ScriptEngineManager
и ScriptEngine
.Мне нужен способ настройки привязок, чтобы они не мешали другим экземплярам.
Returned -> Row is 4
Returned -> Row is 4
Returned -> Row is 4
Returned -> Row is 4
Returned -> Row is 8
Returned -> Row is 5
Returned -> Row is 6
Returned -> Row is 7
Returned -> Row is 8
Returned -> Row is 9
Я мог бы передавать специфичные для строки данные в каждую функцию как переменные, но это становится очень грязным, так как мой пример не 'Я не могу судить о количестве данных, которые мне нужно обменять.Функции также вызываются пользовательскими скриптами, что делает это очень трудным.Я действительно хочу найти способ переопределить obj
в каждом потоке, прежде чем оценивать sJsLocal
.
Это все работает нормально, если я обрабатываю каждый ряд один за другим без многопоточности, но я пытаюсь ускорить этонемного.