您如何杀死Java中的线程?


374

您如何java.lang.Thread用Java 杀死A ?


2
到目前为止,您无法杀死线程;因为destroy()方法是从来没有实现过,由于死锁容易
AZ_

1
我喜欢有关ExecutorStatus以下问题的答案:stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Kirby

9
@loungerdork“我认为Java应该为您无法控制的失控线程实现安全的停止/销毁方法,尽管会丢失锁和其他陷阱,这是不安全的警告”,因此您想要不安全的线程停止。我想你已经有一个。
DJClayworth 2015年

4
令人惊讶的是,2009年什么样的问题将获得212次投票。今天,这些问题将立即被销毁。
乔纳森·莱因哈特

7
@JonathonReinhart:为什么呢?即使在这些日子里,这似乎也是一个合法的问题。也许您不知道当线程失控而只能使用不推荐使用的函数来处理这种情况时,它会导致沮丧吗?
TFuto

Answers:


189

有关他们为何不赞成使用Sun的内容,Thread.stop()请参见。它详细介绍了为什么这是一个不好的方法,以及通常应该怎样做才能安全地停止线程。

他们建议的方法是使用共享变量作为标志,要求后台线程停止。然后可以由另一个请求线程终止的对象来设置此变量。


1
如果您检查已中断isAlive()的线程,它将返回true,并且它们将继续添加到当前ThreadGroup []中,您可以使用Thread.currentThread.getThreadGroup()。list()查看此内容。它会打印出它拥有的所有线程,并且如果您重复执行流程,您将看到线程的多个实例。
AZ_11年

3
如果您在PC上,那没问题,但是如果您正在开发移动版软件(Android我已经体验过),则将收到OutOfMemoryError
AZ_11年

2
应该/应该指出的是,为了确保通过标志快速传达停止请求,变量必须是可变的(或对变量的访问必须同步),如建议中所述。
mtsz 2011年

2
该链接已被杀死。我能找到它archive.org,虽然:web.archive.org/web/20090202093154/http://java.sun.com/j2se/...
周杰伦泰勒

2
我使用的方法getConnection()java.sql.DriverManager。如果连接attemt花费的时间太长,我尝试通过调用杀死相应的线程,Thread.interrupt()但它根本不影响该线程。该Thread.stop()尽管甲骨文表示,如果它不应该工作但工作,interrupt()没有。我想知道如何使其工作并避免使用不推荐使用的方法。
Danny Lo)

129

通常你不会..

您使用Thread.interrupt()要求它中断正在执行的操作(javadoc链接)

为何在此处的Javadoc中有一个很好的解释(java技术说明链接)


@Fredrik interrupt()调用方法时,线程上下文会发生什么?主要问题与每个新线程的日志生成有关。
ABcDexter

3
@ABcDexter整个问题是中断不会中断任何事情,它只是向线程中的代码(或线程正在调用的代码)发出信号,表明有人要求它中断正在执行的任何操作。然后,该线程应该很好地停止处理并返回,就像它已经完成了应做的事情一样(此时,线程上下文可能也被丢弃了)。OTOH,如果您真的强行停止了线程,那么您的问题会很好,答案也不确定。
Fredrik'9

64

在Java中,不会杀死线程,但是以协作方式完成线程的停止。要求线程终止,然后线程可以正常关闭。

通常,一个volatile boolean字段用于该线程定期检查和终止时,它被设置到相应的值。

不会使用a boolean来检查线程是否应该终止。如果您使用volatile的场调节剂,这将工作可靠,但如果你的代码变得更加复杂,对于而是使用内的其他阻止方法while循环,它可能发生,你的代码将不会终止所有或至少需要更长的时间,你可能想要。

某些阻止库方法支持中断。

每个线程都有一个布尔标志中断状态,您应该利用它。可以这样实现:

public void run() {
   try {
      while (!interrupted()) {
         // ...
      }
   } catch (InterruptedException consumed)
      /* Allow thread to exit */
   }
}

public void cancel() { interrupt(); }

源代码来自Java Concurrency in Practice。由于该cancel()方法是公共的,因此您可以让另一个线程根据需要调用此方法。


如果您将不受信任的代码作为插件或脚本运行,该怎么办?Java嵌入了用于不受信任的代码的沙箱。而且那个沙箱是无用的,它允许工作而无需强行停止。假设您正在用Java编写浏览器。杀死任意页面脚本的能力是无价的。
ayvango 2015年

@ayvango然后,您必须在自己的沙箱中运行该脚本。Java沙箱可以保护计算机免受应用程序的攻击,而不是保护应用程序的各个部分之间的相互保护。
David Schwartz

@DavidSchwartz您的意思是说,我应该只使用Java之外的其他平台?
ayvango

@ayvango您仍然可以使用Java沙箱来保护计算机不受应用程序的攻击。但是,如果您想保护应用程序的某些部分免受应用程序的其他部分的影响,则需要选择一些可以做到这一点的工具。
David Schwartz

@DavidSchwartz ASFAIK如果平台级别不支持,则此类工具将不存在。但是,当然可以使用完全解释的脚本引擎解决该任务。它可以像erlang一样做减少工作,并做其他事情。
ayvango

20

一种方法是设置类变量并将其用作标记。

Class Outer {
    public static volatile flag = true;

    Outer() {
        new Test().start();
    }
    class Test extends Thread {

        public void run() {
            while (Outer.flag) {
                //do stuff here
            }
        }
    }

}

设置一个外部类变量,即上例中的flag = true。将其设置为false可以“杀死”线程。


2
就像一个提示:变量as标志仅在线程运行且不卡住时起作用。Thread.interrupt()应该使线程脱离大多数等待条件(等待,睡眠,网络读取等)。因此,您绝不应该抓住InterruptedException来完成这项工作。
ReneS

12
这是不可靠的;制作“标志” volatile以确保它在任何地方都能正常工作。内部类不是静态的,因此标志应为实例变量。该标志应在访问器方法中清除,以便可以执行其他操作(如中断)。名称“标志”不是描述性的。
erickson

2
我在run方法中没有得到“ while”信息。这是否意味着将重复运行方法中写入的所有内容?这不是我们想要线程首先执行的操作:(

2
+1时更喜欢(!Thread.currentThread()。isInteruppted())
Toby

2
两种情况均失败,例如,当您在while {// open ext process}中打开一个外部进程并挂起该进程时,现在该线程将不会被中断,也不会到达末尾以检查布尔条件,而您左挂...尝试一下,例如使用java.exec启动python控制台,并尝试在不写退出的情况下取回控件,看看是否有办法杀死该进程并退出....没有办法摆脱这种情况...
Space Rocker '10

11

有一种方法可以做到。但是,如果您必须使用它,则说明您是一个不好的程序员,或者您正在使用一个由不好的程序员编写的代码。因此,您应该考虑停止成为一个不良的程序员或停止使用此不良代码。此解决方案仅适用于别无选择的情况。

Thread f = <A thread to be stopped>
Method m = Thread.class.getDeclaredMethod( "stop0" , new Class[]{Object.class} );
m.setAccessible( true );
m.invoke( f , new ThreadDeath() );

2
完全没有理由这样做,因为Thread.stop即使不赞成使用,仍然可以致电公众。
Lii 2015年

1
@Lii Thread.stop会执行相同的操作,但还会检查访问和权限。使用Thread.stop是很明显的,我不记得我为什么使用它的原因Thread.stop0。也许Thread.stop不适用于我的特殊情况(Java 6上的Weblogic)。也许是因为Thread.stop已弃用并引起警告。
VadimPlatonov

1
在我的场景中,这是停止不断运行的线程的唯一方法。由于某种原因.stop()并未停止线程,但
stop0

10

我想根据已积累的评论添加一些意见。

  1. Thread.stop() 如果安全管理器允许,它将停止线程。
  2. Thread.stop()是危险的。话虽如此,如果您在JEE环境中工作并且无法控制所调用的代码,则可能有必要;请参阅为什么不赞成使用Thread.stop?
  3. 您永远都不应停止停止容器工作线程。如果要运行易于挂起的代码,请(小心地)启动一个新的守护程序线程并对其进行监视,并在必要时将其杀死。
  4. stop()ThreadDeathError调用线程上创建一个新错误,然后将该错误引发到目标线程上。因此,堆栈跟踪通常毫无价值。
  5. 在JRE 6中,请stop()与安全管理器联系,然后stop1()调用stop0()stop0()是本机代码。
  6. 从Java 13 Thread.stop()开始,尚未删除,但是Thread.stop(Throwable)在Java 11中已删除。(邮件列表JDK-8204243

8

我投赞成票Thread.stop()

例如,您需要进行长时间的操作(例如网络请求)。假设您正在等待响应,但是这可能需要一些时间,并且用户会导航到其他UI。现在,该等待线程是a)无用的b)潜在的问题,因为当他得到结果时,它是完全无用的,并且他将触发可能导致错误数量的回调。

所有这些,他可以进行可能占用大量CPU资源的响应处理。作为开发人员,您甚至无法停止它,因为您不能if (Thread.currentThread().isInterrupted())在所有代码中都加行。

因此无法强制停止一个奇怪的线程。


如果网络操作已经可以安全地中止,则只需中断线程即可。如果无法安全终止网络操作,则Thread.stop()无论如何都无法安全呼叫。您不是在投票Thread.stop(),而是在要求执行每个操作的人员,这些操作可能需要很长时间才能使其安全终止。那可能是个好主意,但与实现Thread.stop()安全堕胎的方式无关。我们已经拥有interrupt了。
David Schwartz

“因此无法强行停止奇怪的线程。” -...直到您深入了解(如Java设计人员所做的那样)并得出结论,没有技术上可行的解决方案不会比弃用更糟stop
Stephen C

5

问题相当模糊。如果您的意思是“我该如何编写程序以使线程在需要时停止运行”,那么各种其他响应都将有所帮助。但是,如果您的意思是“我的服务器出现紧急情况,我现在无法立即重启,而我只需要一个特定的线程就死了,那就可能了”,那么您需要一个干预工具来匹配诸如的监视工具jstack

为此,我创建了jkillthread。请参阅其使用说明。


谢谢!正是我想要的!
丹尼斯·科科林

4

当然,在某些情况下,您正在运行某种不完全可信的代码。(我个人是通过允许在Java环境中执行上载的脚本来实现此目的的。是的,到处都响起了安全警报,但这是应用程序的一部分。)在这种不幸的情况下,首先,您只是希望通过请脚本编写者来实现尊重某种布尔型运行/不运行信号。唯一不错的故障保护是,如果运行时间超过某个超时时间,则在线程上调用stop方法。

但是,这仅仅是“体面的”,而不是绝对的,因为代码可能会捕获ThreadDeath错误(或您明确抛出的任何异常),而不是像绅士线程那样去抛出它。因此,最重要的是AFAIA,没有绝对的故障保护功能。


AFAIK越来越多的服务正在变成混合服务,并使用托管环境来执行他们无法完全控制代码的第三方代码(插件,脚本等),似乎完全不理会线程是不合理的。因为对于服务工程师而言,有效的服务状态可以比不服务的状态无限好(由于挂起(占用线程)或繁忙的无限循环(占用核心))
Weipeng L

3

没有办法优雅地杀死线程。

您可以尝试中断线程,一种常见策略是使用毒丸向线程发送消息以使其自行停止

public class CancelSupport {
    public static class CommandExecutor implements Runnable {
            private BlockingQueue<String> queue;
            public static final String POISON_PILL  = stopnow”;
            public CommandExecutor(BlockingQueue<String> queue) {
                    this.queue=queue;
            }
            @Override
            public void run() {
                    boolean stop=false;
                    while(!stop) {
                            try {
                                    String command=queue.take();
                                    if(POISON_PILL.equals(command)) {
                                            stop=true;
                                    } else {
                                            // do command
                                            System.out.println(command);
                                    }
                            } catch (InterruptedException e) {
                                    stop=true;
                            }
                    }
                    System.out.println(“Stopping execution”);
            }

    }

}

BlockingQueue<String> queue=new LinkedBlockingQueue<String>();
Thread t=new Thread(new CommandExecutor(queue));
queue.put(“hello”);
queue.put(“world”);
t.start();
Thread.sleep(1000);
queue.put(“stopnow”);

http://anandsekar.github.io/cancel-support-for-threads/


2

通常,您不会杀死,停止或中断线程(或检查线程是否被中断()),但应使其自然终止。

很简单。您可以在run()方法内将任何循环与(易失性)布尔变量一起使用,以控制线程的活动。您也可以从活动线程返回到主线程以停止它。

这样,您可以优雅地杀死一个线程:)。


1

突然终止线程的尝试是众所周知的不良编程实践,也是不良应用程序设计的证据。多线程应用程序中的所有线程都显式和隐式共享同一进程状态,并被迫相互协作以使其保持一致,否则,您的应用程序将容易出现难以诊断的错误。因此,开发人员有责任通过仔细而清晰的应用程序设计来保证这种一致性。

对于受控线程终止,有两种主要的正确解决方案:

  • 使用共享易失性标志
  • 使用Thread.interrupt()和Thread.interrupted()方法对。

可以在以下位置找到与突然终止线程有关的问题的详尽详尽说明,以及受控线程终止的错误和正确解决方案的示例:

https://www.securecoding.cert.org/confluence/display/java/THI05-J.+Do+not+use+Thread.stop%28%29+to+terminate+threads


由于程序不再由单个开发人员编写,因此通常有必要杀死线程。因此不能认为是不良的编程。我无法想象没有杀戮的Linux。无法杀死线程是Java的缺陷。
Pavel Niedoba'3

1
太棒了,对吧...如果您必须调用某些第三方库,该库您无法控制,并且存在错误的超时,并且在执行时可能每1000-2000次挂起一次,该怎么办?不好的编程习惯吧?好吧,您不能总是拥有对所使用代码的访问权限。无论如何,操作人员在设计自己的代码时询问如何杀死线程而不是如何控制线程的流……
Arturas M

问题是,当您杀死一个拥有互斥锁或已分配内存块的线程,或者应该杀死另一个线程正在等待的事件或数据时,将会发生什么?应用程序的其余逻辑将如何处理?您将始终面临将微小的明显问题转换为复杂问题的风险,而这些问题将很难重现和调查。杀死线程是不安全的,因为它会使您的应用程序处于许多不同的不一致状态。查看cert.org上的链接中的信息。
ZarathustrA'5

我们有一个控制线程,在某些条件下可能会决定整个计算变得毫无意义。需要在任何方便的地方使用isInterrupted()的一些变体来散布进行实际工作的许多不同可运行对象-多么可怕!如果控制器使ThreadDeath看起来异常冒出来,干净地展开所有已同步的并最终阻止的块,那就更好了。每个人都说这很容易出错,但是对于不相关的线程,我不知道为什么。
丹尼尔


1

我没有在Android上使用该中断,因此我使用了这种方法,效果很好:

boolean shouldCheckUpdates = true;

private void startupCheckForUpdatesEveryFewSeconds() {
    Thread t = new Thread(new CheckUpdates());
    t.start();
}

private class CheckUpdates implements Runnable{
    public void run() {
        while (shouldCheckUpdates){
            //Thread sleep 3 seconds
            System.out.println("Do your thing here");
        }
    }
}

 public void stop(){
        shouldCheckUpdates = false;
 }

2
如果编译器使用线程本地存储进行优化,则应该将volatile关键字添加到shouldCheckUpdates。
clinux18 '19

1

“杀死线程”不是正确的用法。这是我们可以按意愿实现线程的正常完成/退出的一种方法:

我使用过的Runnable:

class TaskThread implements Runnable {

    boolean shouldStop;

    public TaskThread(boolean shouldStop) {
        this.shouldStop = shouldStop;
    }

    @Override
    public void run() {

        System.out.println("Thread has started");

        while (!shouldStop) {
            // do something
        }

        System.out.println("Thread has ended");

    }

    public void stop() {
        shouldStop = true;
    }

}

触发类:

public class ThreadStop {

    public static void main(String[] args) {

        System.out.println("Start");

        // Start the thread
        TaskThread task = new TaskThread(false);
        Thread t = new Thread(task);
        t.start();

        // Stop the thread
        task.stop();

        System.out.println("End");

    }

}

如果编译器使用线程本地存储进行了优化,则应该将volatile关键字添加到shouldStop变量中。
Oleksii Kyslytsyn

0

不推荐使用Thread.stop,那么如何在Java中停止线程?

始终使用中断方法和将来请求取消

  1. 例如,当任务响应中断信号时,使用阻塞队列获取方法。
Callable < String > callable = new Callable < String > () {
    @Override
    public String call() throws Exception {
        String result = "";
        try {
            //assume below take method is blocked as no work is produced.
            result = queue.take();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return result;
    }
};
Future future = executor.submit(callable);
try {
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    logger.error("Thread timedout!");
    return "";
} finally {
    //this will call interrupt on queue which will abort the operation.
    //if it completes before time out, it has no side effects
    future.cancel(true);
}
  1. 当任务不响应中断信号时,假设任务执行了不响应中断信号的套接字I / O,因此使用上述方法不会中止任务,将来会超时,但在finally块中取消将无效,线程将继续监听套接字。如果池实现了,我们可以关闭套接字或在连接上调用close方法。
public interface CustomCallable < T > extends Callable < T > {
    void cancel();
    RunnableFuture < T > newTask();
}

public class CustomExecutorPool extends ThreadPoolExecutor {
    protected < T > RunnableFuture < T > newTaskFor(Callable < T > callable) {
        if (callable instanceof CancellableTask)
            return ((CancellableTask < T > ) callable).newTask();
        else
            return super.newTaskFor(callable);
    }
}

public abstract class UnblockingIOTask < T > implements CustomCallable < T > {
    public synchronized void cancel() {
        try {
            obj.close();
        } catch (IOException e) {
            logger.error("io exception", e);
        }
    }

    public RunnableFuture < T > newTask() {
        return new FutureTask < T > (this) {
            public boolean cancel(boolean mayInterruptIfRunning) {
                try {
                    this.cancel();
                } finally {
                    return super.cancel(mayInterruptIfRunning);
                }
            }

        };
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.