中断运行nashorn脚本的java线程 [英] Interrupt java thread running nashorn script

查看:232
本文介绍了中断运行nashorn脚本的java线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,我有一个javascript运行在与主要的一个单独的线程中。该脚本是一个无限循环,所以它需要以某种方式终止。如何?

In the code below i have javascript running in a separate thread from the main one. That script is an infinite loop, so it needs to be terminated somehow. How?

脚本开始运行后,调用.cancel()不起作用。但是如果我在线程初始化之后调用.cancel(),它将终止它(注释掉的行)。

Calling .cancel() is not working AFTER the script begins running. But if i call .cancel() just after the thread initialization, it will terminate it (the commented out line).

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


推荐答案

JavaScript (在Nashorn下),像Java一样,在紧密循环中不会响应中断。脚本需要轮询中断并自动终止循环,或者它可以调用检查中断的内容并让 InterruptedException 传播。

JavaScript (under Nashorn), like Java, will not respond to an interrupt in the middle of a tight loop. The script needs to poll for interruption and terminate the loop voluntarily, or it can call something that checks for interruption and let InterruptedException propagate.

你可能认为Nashorn只是在运行一个脚本并且应该立即中断。这不适用,因为它不适用于Java:异步中断可能会损害应用程序数据结构的损坏,并且基本上没有办法避免它或从中恢复。

You might think that Nashorn is "just running a script" and that it should be interrupted immediately. This doesn't apply, for the same reason that it doesn't apply in Java: asynchronous interruption risks corruption of the application's data structures, and there is essentially no way to avoid it or recover from it.

异步中断带来的问题与长期弃用的 Thread.stop 方法相同。本文档对此进行了解释,该文档是评论中链接的文档的更新版本。

Asynchronous interruption brings in the same problems as the long-deprecated Thread.stop method. This is explained in this document, which is an updated version of the document linked in the comments.

Java Thread Primitive Deprecation

参见Goetz, Java Concurrency In Practice ,第7章,取消和关闭

See also Goetz, Java Concurrency In Practice, Chapter 7, Cancellation and Shutdown.

检查中断的最简单方法是调用 Thread.interrupted()。您可以通过JavaScript轻松调用它。这是重写示例程序,在五秒后取消正在运行的脚本:

The easiest way to check for interruption is to call Thread.interrupted(). You can call this quite easily from JavaScript. Here's a rewrite of the example program that cancels the running script after five seconds:

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

由于我们正在启动一个线程池,可能并使其成为一个预定的线程池,以便我们可以将它用于脚本任务和超时。这样我们可以避免 Timer TimerTask ,它们大多被 ScheduledExecutorService 无论如何。

Since we're starting up a thread pool, might as well make it a scheduled thread pool so that we can use it for both the script task and the timeout. That way we can avoid Timer and TimerTask, which are mostly replaced by ScheduledExecutorService anyway.

处理和中断时的通常惯例是恢复中断位或让 InterruptedException 传播。 (一个人应该从不忽略一个中断。)由于断开循环可以被认为已经完成了中断的处理,两者都没有必要,只是让脚本正常退出似乎就足够了。

The usual convention when handling and interrupt is either to restore the interrupt bit or to let an InterruptedException propagate. (One should never ignore an interrupt.) Since breaking out of the loop can be considered to have completed the handling of the interrupt, neither is necessary, and it seems sufficient simply to let the script exit normally.

这次重写还会将构造函数中的大量工作转移到 init()方法中。这可以防止实例从构造函数中泄漏到其他线程。在原始示例代码中没有明显的危险 - 实际上,几乎从来没有 - 但是避免从构造函数中泄漏实例总是很好的做法。

This rewrite also moves a lot of work out of the constructor into an init() method. This prevents the instance from being leaked to other threads from within the constructor. There is no obvious danger from this in the original example code -- in fact, there almost never is -- but it's always good practice to avoid leaking the instance from the constructor.

这篇关于中断运行nashorn脚本的java线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆