我有一种情况,正在处理用户提供的包含JavaScript部分的模板。我正在使用Java的intent.extras?.putByteArray(event, response.toByteArray())
[...]
var list = Arrays.asList(intent.extras?.getByteArray(event))
来评估那些JavaScript。我有各种各样的助手功能,我只希望对其进行一次评估(将这些助手功能视为加载ScriptEngine
之类的东西)。
一旦加载完成,我便遍历数据行并处理每一行的模板-这些模板可以使用任何先前加载的帮助器函数。我正在尝试并行处理行,因为在这种情况下Java的Underscore.js
很慢。
任何通过简化的SSCCE进行研究的人都会知道,我正在以非线程安全的方式修改全局对象,从而获得了预期的结果。在我的示例中,ScriptEngine.eval()
是此全局JavaScript。全局函数需要访问我放置在sJsGlobal
中的各种行特定的数据位。
要使obj
成功sJsGlobal
,它需要了解eval()
,否则将会失败。在我的SSCCE中,每个线程在调用函数之前都更新相同的全局obj
。我正在为每个全局函数寻找一种查看本地obj
的方法。这是Catch-22的情况,我需要为全局脚本定义变量才能知道它的存在,但我希望它具有局部意义。
obj
这将导致以下输出,由于我使用此全局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
的方法。
如果我逐行处理每一行而不使用线程,那么一切都很好,但是我正在尝试加快速度。
答案 0 :(得分:0)
经过大量的尝试,错误和努力,并且阅读了许多相似的问题而没有答案,我提出了以下似乎有效的方法,并在此处记录了遇到此问题的其他任何人。这使用一个公用的ScriptEngine
和每个线程一个单独的ScriptContext
来提供隔离。
import java.util.*;
import java.util.concurrent.*;
import javax.script.*;
public class SSCCE {
public static void main(String[] args) throws Exception {
ScriptEngine js = new ScriptEngineManager().getEngineByName("JavaScript");
StringBuilder sJsGlobal = new StringBuilder();
sJsGlobal.append("var obj = {};");
sJsGlobal.append("function get_row() { return \"Row is \" + obj.row; }");
CompiledScript jsLib = ((Compilable)js).compile(sJsGlobal.toString());
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();");
ScriptContext scLocal = new SimpleScriptContext();
scLocal.setBindings(js.createBindings(), ScriptContext.ENGINE_SCOPE);
jsLib.eval(scLocal);
js.eval(sJsLocal.toString(), scLocal);
return (String)scLocal.getAttribute("x");
}));
}
for (Future<String> f : af) {
System.out.println("Returned -> " + f.get());
}
es.shutdown();
}
}
这将在运行时产生以下结果:
Returned -> Row is 0
Returned -> Row is 1
Returned -> Row is 2
Returned -> Row is 3
Returned -> Row is 4
Returned -> Row is 5
Returned -> Row is 6
Returned -> Row is 7
Returned -> Row is 8
Returned -> Row is 9