【问题标题】:Java Thread won't stopJava 线程不会停止
【发布时间】:2012-07-24 04:11:01
【问题描述】:

我有一个评估一些脚本的 JRuby 引擎,如果它需要超过 5 秒,我想关闭线程。 我尝试过这样的事情:

class myThread extends Thread{
    boolean allDone = false;

    public void threadDone() {
        allDone = true;
    }

    public void run() {
        while(true) {
            engine.eval(myScript);
            if(allDone)
                return;
        }
    }

(...)

    th1 = new myThread();
    th1.start();
    try {
        Thread.sleep(5000);
        if(th1.isAlive())
            th1.threadDone();
    } catch(InterruptedException e) {}

    if(th1.isAlive())
        System.out.println("Still alive");

我也尝试使用th1.stop()th1.interrupt() 杀死线程,但th1.isAlive() 方法返回的值始终是true

我能做什么? 我想补充一点,myScript 可能是“while(1) do; end”,我不能等到它完成。所以我想阻止这样的脚本,如果它需要超过 5 秒就杀死线程。

【问题讨论】:

  • 不要extends Thread。这是众所周知的反模式。
  • 它正在运行一个脚本,对吧?所以你需要停止它运行脚本。如果将“myScript”设置为 null 会发生什么?它会立即在线程中引发异常吗?
  • 不,它没有。如果我将 'myScript' 设置为 "" 它会循环。它的行为类似于“while(1) do; end”。
  • @user1521526 - 值得一试:(

标签: java multithreading jruby


【解决方案1】:

另一种解决方案是使用内置机制来中断线程:

public void run() {
    while (!Thread.currentThread().isInterrupted()) {
        engine.eval(myScript);
    }
}

...
th1 = new myThread();
th1.start();
try {
    Thread.sleep(5000);
    th1.interrupt();
} 

这样,不需要allDone 字段,也没有同步失败的风险。

【讨论】:

  • 看来您可以将该条件放入while 而不是true
  • 确实如此。我只是复制并粘贴了原始代码,没有引起太多注意。但我会解决的。
  • 似乎习惯用法是只检查Thread.interrupted(),为自己节省了一个间接性。该调用还清除了中断标志,这应该没问题——一旦你发现你被中断了,你就重置interrupted信号标志。 (我在前面的问题中读到过这个)。
  • 恕我直言,最好避免清除标志。在这种特殊情况下,它不会改变任何东西,但假设这个循环在一个方法内部,该方法本身在 run() 方法的循环中调用,您希望尽快退出内部循环,并保留中断标志这样外循环也可以尽快退出。
  • 是的,整个系统的设计方式充满了陷阱。一般如果不能立即保证终止,应该抛出InterruptedException
【解决方案2】:

要使您的线程可停止,您可能需要类似的东西。

class MyTask implements Runnable {
    public void run() {
        try {
           engine.eval(myScript);
        } catch(ThreadDeath e) {
           engine = null; // sudden death.
        }
    }    
}

您可以调用 Thread.stop(),但我建议您先阅读有关此方法的警告。


如果您希望线程最多运行 5 秒,最简单的解决方案是让线程自行停止。

class MyTask implements Runnable {
    public void run() {
        long start = System.currentTimeMillis();
        do {
           engine.eval(myScript);
        } while(System.currentTimeMillis() < start + 5000);
    }    
}

这假设您想要重复运行 engine.eval()。如果不是这种情况,您可能必须 stop() 线程。它已被弃用是有充分理由的,但它可能是您唯一的选择。

【讨论】:

  • Peter 我只想运行一次 engine.eval()。当用户尝试评估诸如“while(1) do;end”之类的脚本时,就会出现问题。所以我想防止这种情况发生并在超过 5 秒时终止线程。
  • 在这种情况下,Thread.stop() 是你唯一的选择。这可能会使您的引擎处于不一致的状态,因此您可能需要重新创建它。
  • 好吧,我尝试使用 Thread.stop() 但线程仍然存在。 (Thread.isAlive() 方法返回 true)