如何找到并停止所有当前正在运行的线程?

Posted

技术标签:

【中文标题】如何找到并停止所有当前正在运行的线程?【英文标题】:How to find and stop all currently running threads? 【发布时间】:2013-03-15 11:51:57 【问题描述】:

我有一个多线程 java 项目,我想添加一个方法 stop() 来停止所有正在运行的线程。问题是这个项目是别人开发的,我不熟悉它是如何实现多线程的。

我所知道的是,一旦项目启动,就会调用许多线程并且它们会永远运行。有没有办法找到所有正在运行的线程并停止它们?我搜索了很多,找到了如何获取正在运行的线程列表:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();

接下来要做什么来停止所有正在运行的线程?

我之所以要停止这些线程,是因为我需要将此项目作为捆绑包部署到 OSGi 容器中。捆绑包启动后,多个线程将永远运行。所以我需要实现一个destroy()方法来停止所有线程来控制bundle的生命周期。

怎么样

for (Thread t : Thread.getAllStackTraces().keySet()) 
  if (t.getState()==Thread.State.RUNNABLE) 
     t.interrupt(); 
 

for (Thread t : Thread.getAllStackTraces().keySet()) 
  if (t.getState()==Thread.State.RUNNABLE) 
     t.stop(); 

【问题讨论】:

为什么要停止所有线程?如果您都希望停止您的应用程序,那么 System.exit 是正确的选择。 不要试图停止所有线程。 Thread 的 stop() 已被弃用是有原因的。查看文档:docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#stop() @Ankit 因为我需要将此项目作为捆绑包部署到 OSGi 容器。捆绑包启动后,多个线程将永远运行。所以我需要实现一个destroy()方法来停止所有线程来控制bundle的生命周期。 你不能使用 OSGi 捆绑停止方法本身。我对 OSGi 不太了解,如果不是很有用,请忽略我的评论。 为什么会有永远运行的线程?它们是您创建的线程吗? 【参考方案1】:

这是一个危险的想法。 Thread.stop() 的 Javadoc 解释说:

这种方法本质上是不安全的。使用 Thread.stop 停止线程会导致它解锁所有已锁定的监视器(这是未经检查的 ThreadDeath 异常沿堆栈传播的自然结果)。如果以前受这些监视器保护的任何对象处于不一致状态,则损坏的对象将对其他线程可见,从而可能导致任意行为。停止的许多用法应该由简单地修改一些变量以指示目标线程应该停止运行的代码替换。目标线程应该定期检查这个变量,如果变量指示它要停止运行,则以有序的方式从它的run方法返回。如果目标线程等待很长时间(例如在条件变量上),则应使用中断方法来中断等待。

从根本上说,线程需要被构建和设计为安全终止,不可能安全地杀死任意线程。一个相当标准的模式是这样实现的:

public abstract class StoppableRunnable implements Runnable 
    private volatile boolean stopWork;
    private boolean done;

    public final void run() 
        setup();
        while(!stopWork && !done) 
            doUnitOfWork();
        
        cleanup();
    

    /**
     * Safely instructs this thread to stop working,
     * letting it finish it's current unit of work,
     * then doing any necessary cleanup and terminating
     * the thread.  Notice that this does not guarentee
     * the thread will stop, as doUnitOfWork() could
     * block if not properly implemented.
     */
    public void stop() 
        stopWork = true;
    

    protected void done() 
        done = true;
    

    protected void setup()  
    protected void cleanup()  

    /**
     * Does as small a unit of work as can be defined
     * for this thread.  Once there is no more work to
     * be done, done() should be called.
     */
    protected abstract void doUnitOfWork();

您暗示您不是这些线程的作者,这表明它们可能无法安全停止。在这种情况下,您可以调用Thread.interrupt() 来指示线程停止它正在做的事情(而不是上面描述的模式,您可以使用Thread.interrupt() 达到类似的效果)但是类似地,如果线程的设计者没有编写它为了处理中断,这可能不会做任何事情或导致不一致的状态或其他错误。

如果您只想“[强制]线程停止执行”并且不能修改线程的实现,那么最终,Thread.stop() 就是您想要的;然而,就像在 Unix 中使用 kill 一样,这是一个危险的提议,您应该从本质上考虑您的 JVM 在以这种方式终止线程后处于不稳定和不可修复的状态,然后尝试尽快退出程序。


关于你打断然后停止的建议:

这里还有很多问题,特别是中断并不能保证线程会立即中断(它的工作方式与我上面的StoppableRunnable 类似,但不太明确),而是设置一个标志,表明线程应该在何时中断可能的。这意味着您可以调用Thread.interrupt(),线程可以启动它正确的中断处理行为,然后在中途,您对Thread.stop() 的调用触发,猛烈地杀死线程并可能破坏您的JVM。对Thread.interrupt() 的调用不能保证线程何时或如何响应该中断,这就是为什么我更喜欢StoppableRunnable 中的显式行为。不用说,如果您要致电Thread.stop(),首先致电Thread.interrupt() 并没有什么好处。我不推荐它,但你不妨一开始就打电话给Thread.stop()

此外,要认识到运行循环的代码本身就在一个线程中 - 这意味着你的循环很可能首先杀死自己,让所有其他线程继续运行。

【讨论】:

我可以试试这个:for (Thread t : Thread.getAllStackTraces().keySet()) if (t.getState()==Thread.State.RUNNABLE) t.interrupt(); for (线程 t : Thread.getAllStackTraces().keySet()) if (t.getState()==Thread.State.RUNNABLE) t.stop(); 如果我没看错(更新你的问题比在 cmets 中发布代码更好)应该给你正在运行的线程数,是的。但它不会阻止他们中的任何一个...... 在第一个 for() 循环中,我调用 t.interrupt() 来指示线程停止。在第二个 for() 循环中,我调用 t.stop() 来停止线程。我知道这是个坏主意,但我别无选择,因为我不是创建这些线程的作者 当您继续编辑 cmets 时,我很难理解您要执行的操作。如果您有其他信息,请更新您的问题。您当然可以尝试一下,就像我在回答中所说的那样,调用 Thread.stop() 本质上是危险的,并且可能会使您的应用程序处于损坏状态。如果您可以接受,那就去做吧,但是当您开始看到异常/难以调试的错误和行为时不要感到惊讶。 这与竞争条件无关。每个线程都可以在自己的核心上运行并使用其缓存。只有当您通过内存屏障(易失性读取或写入以及同步)时,这些缓存才会同步。因此,如果您在一个线程中并检查在另一个线程上更改的私有 var,您将永远不会在多核上看到它。 Brian Goetz 关于并发的书给出了很好的解释。【参考方案2】:

我使用了这个代码:

package com.xxx;

import com.xxx.tools.messageLog;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author Mostafa Nazari
 */
public class ProcessAgent extends Thread

    public ProcessAgent() 
        super("Process-Agent");
    


    @Override
    public void run() 
        try 
            com.xxx.Process.agent.Main.main(new String[]);
         catch (Exception ex) 
            if (ex instanceof InterruptedException) 
                messageLog.stdwar("Process Agent Stopped");
            else 
                messageLog.stderr("FATAL Error: "+ex.getMessage());
                Logger.getLogger(ProcessAgent.class.getName()).log(Level.SEVERE, null, ex);            
            
            
    

    public void terminate() 
        if (super.isAlive()) 
            ThreadGroup group = super.getThreadGroup();
            while (group != null) 
                group .interrupt();
                group = super.getThreadGroup();
            
            super.interrupt();
        
    

    public String getStatus() 
        if (super.isAlive()) 
            return "running";
         else 
            if (super.isInterrupted()) 
                return "stopping";
            else
                return "stopped";
        
    


希望对这个问题有用

【讨论】:

【参考方案3】:

不要创建普通的Thread,而是使用高级的concurrent API,例如ExecutorService 或ThreadPoolExecutor

您可以在这篇文章中查看不同的 API:

Java's Fork/Join vs ExecutorService - when to use which?

ExecutorService的强制关闭参考下面的SE问题:

How to forcefully shutdown java ExecutorService

【讨论】:

以上是关于如何找到并停止所有当前正在运行的线程?的主要内容,如果未能解决你的问题,请参考以下文章

如何停止一个正在运行的线程?

腾讯面试官:如何停止一个正在运行的线程?我一脸懵逼……

腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。

腾讯面试官:如何停止一个正在运行的线程?我蒙了。。。

腾讯面试官:如何停止一个正在运行的线程?我蒙了。。。

腾讯面试官:如何停止一个正在运行的线程?我一脸蒙蔽。。。