use*_*119 7 javascript java multithreading nashorn
在下面的代码中,我有一个javascript运行在与主要一个单独的线程中.该脚本是一个无限循环,因此它需要以某种方式终止.怎么样?
在脚本开始运行后调用.cancel()不起作用.但是如果我在线程初始化之后调用.cancel(),它将终止它(注释掉的行).
package testscriptterminate;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.Timer;
import java.util.TimerTask;
public class TestScriptTerminate extends TimerTask{
private ExecutorService threads;
private Future runScript;
private Timer t;
public TestScriptTerminate(){
t = new Timer();
t.schedule(this, 6000); //let the script run for a while before attempt to cancel
threads = Executors.newFixedThreadPool(1);
runScript = threads.submit(new ScriptExec());
//runScript.cancel(true); //will cancel here, before the script had a change to run, but useless, i want to cancel at any time on demand
}
@Override
public void run(){
//after script has fully initialized and ran for a while - attempt to cancel.
//DOESN'T WORK, thread still active
System.out.println("Canceling now...");
runScript.cancel(true);
}
public static void main(String[] args) {
new TestScriptTerminate();
}
}
class ScriptExec implements Runnable{
private ScriptEngine js;
private ScriptEngineManager scriptManager;
public ScriptExec(){
init();
}
@Override
public void run() {
try {
js.eval("while(true){}");
} catch (ScriptException ex) {
System.out.println(ex.toString());
}
}
private void init(){
scriptManager = new ScriptEngineManager();
js = scriptManager.getEngineByName("nashorn");
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
所以这很古老,但我只是写下来,认为分享可能很有价值。默认情况下,您无法停止Nashorn脚本的执行,.cancel() Thread.stop() Thread.interrupt()什么也不做,但是如果您愿意付出一些努力并且可以重写某些字节码,那是可以实现的。细节:
http://blog.getsandbox.com/2018/01/15/nashorn-interupt/
JavaScript(在 Nashorn 下)与 Java 一样,不会响应紧密循环中间的中断。该脚本需要轮询中断并自动终止循环,或者它可以调用检查中断并让InterruptedException传播的东西。
您可能认为 Nashorn “只是运行一个脚本”,应该立即中断它。这并不适用,其原因与 Java 中不适用的原因相同:异步中断有损坏应用程序数据结构的风险,并且基本上没有办法避免它或从中恢复。
异步中断带来了与长期不推荐使用的Thread.stop方法相同的问题。本文档对此进行了解释,该文档是评论中链接的文档的更新版本。
另请参阅 Goetz,《Java 并发实践》,第 7 章,取消和关闭。
检查中断的最简单方法是调用Thread.interrupted(). 您可以从 JavaScript 中轻松调用它。下面是示例程序的重写,它在五秒后取消正在运行的脚本:
public class TestScriptTerminate {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);
void script() {
ScriptEngineManager scriptManager = new ScriptEngineManager();
ScriptEngine js = scriptManager.getEngineByName("nashorn");
try {
System.out.println("Script starting.");
js.eval("while (true) { if (java.lang.Thread.interrupted()) break; }");
System.out.println("Script finished.");
} catch (ScriptException ex) {
ex.printStackTrace();
}
}
void init() throws Exception {
Future<?> scriptTask = pool.submit(this::script);
pool.schedule(() -> {
System.out.println("Canceling now...");
scriptTask.cancel(true);
}, 5, TimeUnit.SECONDS);
pool.shutdown();
}
public static void main(String[] args) throws Exception {
new TestScriptTerminate().init();
}
}
Run Code Online (Sandbox Code Playgroud)
由于我们正在启动一个线程池,不妨将其设为一个计划线程池,以便我们可以将它用于脚本任务和超时。这样我们就可以避免Timer和TimerTask,它们大多被替换为ScheduledExecutorService反正。
处理和中断时的通常约定是恢复中断位或让传播InterruptedException。(永远不应该忽略中断。)由于跳出循环可以被认为已经完成了中断的处理,所以两者都不是必要的,并且简单地让脚本正常退出似乎就足够了。
此重写还将构造函数中的大量工作移至init()方法中。这可以防止实例从构造函数内泄漏到其他线程。在原始示例代码中,这并没有明显的危险——事实上,几乎从来没有——但避免从构造函数中泄漏实例总是好的做法。
| 归档时间: |
|
| 查看次数: |
4761 次 |
| 最近记录: |