Как найти и остановить все текущие потоки?

У меня есть многопоточный проект java, и я хочу добавить метод stop(), чтобы остановить все запущенные потоки. Проблема в том, что этот проект разрабатывается кем-то другим, и я не знаком с тем, как он реализует несколько потоков.

Я знаю, что после начала проекта запускается много потоков, и они запускаются вечно. Есть ли способ найти все запущенные потоки и остановить их? Я много искал и нашел, как получить список запущенных потоков:

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

Что делать дальше, чтобы остановить все текущие потоки?

Причина, по которой я хочу остановить эти потоки, заключается в том, что мне нужно развернуть этот проект в контейнер OSGi в виде пакета. Как только пакет запущен, несколько потоков выполняются вечно. Поэтому мне нужно реализовать метод destroy(), чтобы остановить все потоки для управления жизненным циклом пучка.

Как насчет

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

Ответ 1

Это опасная идея. Javadoc для Thread.stop() объясняет:

Этот метод по своей сути является небезопасным. Остановка потока с помощью Thread.stop заставляет его разблокировать все мониторы, которые он заблокировал (как естественное следствие неконтролируемого исключения ThreadDeath, распространяющегося по стеку). Если какой-либо из объектов, ранее защищенных этими мониторами, находился в противоречивом состоянии, поврежденные объекты становятся видимыми для других потоков, что может привести к произвольному поведению. Многие применения stop должны быть заменены кодом, который просто изменяет некоторую переменную, чтобы указать, что целевой поток должен прекратить работу. Целевой поток должен регулярно проверять эту переменную и возвращаться из метода запуска упорядоченным способом, если переменная указывает, что она должна прекратиться. Если целевой поток ожидает длительные периоды (например, для переменной условия), для прерывания ожидания следует использовать метод прерывания.

По сути, потоки должны быть построены и спроектированы таким образом, чтобы безопасно завершать работу, невозможно безопасно убить произвольные потоки. Достаточно стандартный шаблон реализуется так:

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 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() - это то, что вам нужно, если вы просто хотите "[принудительно] поток прекратить выполнение" и не можете изменить реализацию потока; однако, как использование kill в Unix, это опасное предложение, и вы должны по существу считать, что ваш JVM находится в неустойчивом и непоправимом состоянии после завершения потока таким образом и пытается выйти из программы как можно быстрее после этого.


Что касается вашего предложения прерывания, то остановка:

Здесь все еще много проблем, в частности, прерывание не гарантирует, что поток будет прерываться немедленно (он работает аналогично, хотя и менее явно, для моего StoppableRunnable выше) и вместо этого устанавливает флаг, который поток должен прерывать, когда возможное. Это означает, что вы могли бы вызвать Thread.interrupt(), поток мог бы запустить его надлежащее поведение обработки прерываний, а затем на полпути через него, ваш вызов Thread.stop() срабатывает, яростно убивая поток и потенциально нарушая вашу JVM. Вызовы Thread.interrupt() не дают никаких гарантий относительно того, когда или как поток будет реагировать на это прерывание, поэтому я предпочитаю явное поведение в StoppableRunnable. Излишне говорить, что если вы когда-нибудь назовете Thread.stop(), то немного выиграйте, сначала позвонив Thread.interrupt(). Я не рекомендую его, но вы можете просто вызвать Thread.stop() в первую очередь.

Кроме того, признайте, что код, запускающий ваш цикл, сам по себе в потоке - это означает, что ваш цикл может очень хорошо убить себя первым, оставив все остальные потоки.

Ответ 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-приложение Fork/Join vs ExecutorService - когда использовать, который?

См. ниже вопросы SE для принудительного отключения ExecutorService:

Как принудительно завершить работу java ExecutorService