Как вы убиваете нить в Java?


374

Как вы убиваете java.lang.Threadв Java?


2
до сих пор вы не можете убить нить; потому что destroy () никогда не реализуется из-за склонности к блокировке
AZ_

1
Я предпочитаю ответ относительно ExecutorStatusэтого вопроса: stackoverflow.com/questions/2275443/how-to-timeout-a-thread
Кирби

9
@loungerdork "Я думаю, что в Java должен быть реализован безопасный метод остановки / уничтожения для запущенных потоков, который вы не можете контролировать, несмотря на предостережения о потере блокировок и других ловушках" Итак, вы хотите небезопасную остановку потока. Я думаю, что у вас уже есть один.
DJClayworth

4
Удивительно, какие вопросы получили бы 212 голосов в 2009 году. Это было бы немедленно уничтожено сегодня.
Джонатон Рейнхарт

7
@JonathonReinhart: Почему это? Кажется, это законный вопрос, даже в наши дни. Может быть, вы не знаете, какое разочарование это вызывает, когда у вас бегущие потоки, и вы можете использовать только устаревшие функции, чтобы как-то справиться с этим?
TFuto

Ответы:


189

Посмотрите эту ветку от Sun о том, почему они устарелиThread.stop() . Подробно рассказывается о том, почему это был плохой метод, и что нужно сделать для безопасной остановки потоков в целом.

Они рекомендуют использовать общую переменную в качестве флага, который просит фоновый поток остановиться. Затем эта переменная может быть установлена ​​другим объектом, запрашивающим завершение потока.


1
если вы проверите поток, который прервал isAlive (), он вернет вам значение true, и они продолжат добавление в вашу текущую группу ThreadG [], вы можете увидеть это с помощью Thread.currentThread.getThreadGroup (). list (); он напечатает все потоки, которые он имеет, и вы увидите несколько экземпляров вашего потока, если вы повторите свой поток.
AZ_

3
Если вы работаете на ПК, то это не проблема, но если вы разрабатываете программное обеспечение для мобильных устройств (Android я испытал), то вы получите OutOfMemoryError
AZ_

2
Можно / следует отметить, что для обеспечения быстрой передачи запроса остановки через флаг переменная должна быть изменчивой (или доступ к переменной должен быть синхронизирован), как указано в рекомендации.
mtsz

2
Эта ссылка была убита в этот момент. Я смог найти его на archive.org, хотя: web.archive.org/web/20090202093154/http://java.sun.com/j2se/…
Джей Тейлор,

2
Я использую метод getConnection()из java.sql.DriverManager. Если проверка соединения занимает слишком много времени, я пытаюсь уничтожить соответствующий поток, вызвав Thread.interrupt()его, но это никак не влияет на поток. Thread.stop()Работает , однако, хотя оракул говорит , что это не должно работать , если interrupt()нет. Интересно, как заставить это работать и избежать использования устаревшего метода.
Дэнни Ло

129

Как правило, вы не ..

Вы просите его прервать все, что он делает, используя Thread.interrupt () (ссылка на Javadoc)

Хорошее объяснение того, почему в Javadoc здесь (ссылка Java Technote)


@Fredrik Что происходит с контекстом Thread при interrupt()вызове метода? Основной вопрос связан с созданием журнала для каждой новой темы.
ABcDexter

3
@ABcDexter Суть в том, что прерывание ничего не прерывает, оно просто сообщает коду в потоке (или к коду, вызываемому потоком), что кто-то попросил его прервать все, что он делает. Затем предполагается, что поток приятно останавливает обработку и возвращает, точно так же, как если бы он был сделан, делая то, что должен (и в этот момент контекст потока, вероятно, также отбрасывается). OTOH, если бы вы действительно принудительно остановили поток, ваш вопрос был бы действительно хорошим, а ответ не определен.
Фредрик

64

В Java потоки не уничтожаются, но остановка потока выполняется кооперативным способом . Поток просят прекратить, и затем поток может корректно завершиться.

Часто используется volatile booleanполе, которое поток периодически проверяет и завершает, когда ему присваивается соответствующее значение.

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

Некоторые блокирующие библиотечные методы поддерживают прерывание.

Каждый поток уже имеет статус прерванного логического флага, и вы должны использовать его. Это может быть реализовано так:

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

public void cancel() { interrupt(); }

Исходный код адаптирован из Java Concurrency на практике . Так как cancel()метод общедоступен, вы можете позволить другому потоку вызывать этот метод так, как вам нужно.


А что делать, если вы запускаете ненадежный код в виде плагина или скрипта? Java имеет встроенную песочницу для ненадежного кода. И эта песочница бесполезна, она позволяет работать без насильственной остановки. Представьте, что вы пишете браузер на Java. Возможность убить произвольную страницу скрипта бесценна.
Айванго

@ayvango Затем вы должны запустить этот скрипт в своей песочнице. Песочница Java защищает компьютер от приложения, а не части приложения друг от друга.
Дэвид Шварц

@DavidSchwartz, ты имеешь в виду, что я просто должен использовать другую платформу, чем Java?
Айванго

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

@DavidSchwartz ASFAIK такие инструменты не могут существовать, если они не поддерживаются на уровне платформы. Но, конечно, эту задачу можно решить с помощью полностью интерпретированного скриптового движка. Он может рассчитывать сокращения, как Эрланг, и делать другие вещи.
Айванго

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 flag работает только тогда, когда поток работает и не застрял. Thread.interrupt () должен освободить поток из большинства условий ожидания (ожидание, сон, чтение по сети и т. Д.). Поэтому вам никогда не следует перехватывать InterruptedException, чтобы это работало.
ReneS

12
Это не надежно; сделать «флаг», volatileчтобы убедиться, что он работает везде. Внутренний класс не является статическим, поэтому флаг должен иметь переменную экземпляра. Флаг должен быть очищен в методе доступа, чтобы можно было выполнять другие операции (например, прерывание). Название «флаг» не носит описательный характер.
Эриксон

2
Я не получаю "время" в методе бега. Не означает ли это, что все, что написано в методе run, будет повторяться? это не то, что мы хотели, чтобы поток сделал в первую очередь :(

2
+1 делать пока (! Thread.currentThread (). IsInteruppted ()) предпочтительнее
Тоби

2
Оба случая терпят неудачу, например, когда вы открываете внешний процесс внутри while {// open ext process} и этот процесс зависает, теперь ни поток не будет прерван, ни достигнут конец для проверки вашего логического условия, и вы осталось висеть ... попробуйте, например, запустить консоль python с помощью java.exec и попытаться вернуть элемент управления без записи exit и посмотреть, есть ли способ убить этот процесс и выйти .... нет способа выйти из такой ситуации ...
Space Rocker

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даже если она устарела.
Лии

1
@Lii Thread.stopделает то же самое, но также проверяет доступ и разрешения. Использование Thread.stopдовольно очевидно, и я не помню причину, почему я использовал Thread.stop0вместо этого. Может быть Thread.stop, не работает для моего особого случая (Weblogic на Java 6). Или, может быть, потому что Thread.stopустарела и вызывает предупреждение.
ВадимПлатонов

1
В моем сценарии это был единственный способ остановить бесконечный поток. Для некоторых Причина .stop () не остановить поток, но stop0 () сделал
fiffy

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().

Например, у вас длительная операция (например, сетевой запрос). Предположительно, вы ждете ответа, но это может занять некоторое время, и пользователь перешел на другой пользовательский интерфейс. Этот ожидающий поток теперь а) бесполезен; б) потенциальная проблема, потому что когда он получит результат, он совершенно бесполезен, и он вызовет обратные вызовы, которые могут привести к количеству ошибок.

Все это, и он может выполнять обработку ответов, которая может сильно загружать процессор. И вы, как разработчик, не можете даже остановить это, потому что вы не можете бросать if (Thread.currentThread().isInterrupted())строки во всем коде.

Так что невозможность принудительно остановить поток это странно.


Если сетевая операция уже благополучно прервана, просто прервите поток. Если работа сети небезопасно прерывается, вы Thread.stop()все равно не сможете безопасно позвонить . Вы не голосуете за Thread.stop(), вы спрашиваете о каждом человеке, который выполняет все операции, которые могут занять много времени, чтобы сделать их безопасными для прерывания. И это может быть хорошей идеей, но это не имеет ничего общего с реализацией Thread.stop()в качестве способа запроса безопасного аборта. У нас уже есть interruptдля этого.
Дэвид Шварц

«Поэтому невозможность принудительно остановить поток это странно». - ... пока вы не посмотрите глубоко (как это сделали дизайнеры Java) и не придете к выводу, что не существует технически жизнеспособных решений, которые не хуже, чем устаревшие stop.
Стивен С

5

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

Для этого я создал jkillthread . Смотрите инструкции по его использованию.


Спасибо! Именно то, что я искал!
Денис Кокорин

4

Конечно, есть случай, когда вы запускаете какой-то не полностью доверенный код. (Лично я имею это, позволяя загруженным сценариям выполняться в моей среде Java. Да, повсюду звучит сигнал тревоги, но это часть приложения.) В этом печальном случае вы, прежде всего, надеетесь, спрашивая авторов сценариев. уважать какой-то логический сигнал «запустить / не запустить». Единственный достойный отказоустойчивый метод - это вызвать метод stop в потоке, если, скажем, он работает дольше, чем некоторое время ожидания.

Но это просто «прилично», и не абсолютно, потому что код может перехватить ошибку ThreadDeath (или любое исключение, которое вы явно выбросили), и не перебрасывать ее, как предполагается, что это делает джентльменский поток. Итак, суть в том, что AFAIA не является абсолютной отказоустойчивостью.


AFAIK все больше и больше сервисов становятся гибридными и используют управляемую среду для выполнения стороннего кода (плагина, сценариев и т. Д.), Который они не имеют полного контроля над кодом, кажется неразумным полностью исключать thread.stop из таблицы, поскольку для сервисных инженеров состояние «живого и обслуживающего» может быть бесконечно лучше, чем состояние «не обслуживающего» (либо из-за зависания (которое забирает потоки), либо из-за занятого бесконечного цикла (которое забирает ядра))
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

Обычно вы не убиваете, не останавливаете и не прерываете поток (или не проверяете, прерван ли он ()), но позволяете ему завершаться естественным образом.

Это просто. Вы можете использовать любой цикл вместе с (volatile) логической переменной внутри метода 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.
Павел Недоба

1
Здорово, мистер Право ... Что если вам нужно вызвать какую-нибудь стороннюю библиотеку, которую вы не можете контролировать и которая имеет ошибочное время ожидания и может зависать один раз каждые 1000-2000 раз при выполнении? Плохая практика программирования, а? Ну, вы не всегда можете иметь доступ к коду, который вы используете. Во всяком случае, операционная система спрашивает, как убить поток, а не как управлять его потоком при разработке собственного кода ...
Arturas M

Вопрос в том, что произойдет, когда вы убьете поток, которому принадлежит мьютекс или которому выделен какой-то блок памяти, или который должен сгенерировать какое-либо событие или данные, которые ожидает другой поток? Что будет с остальной логикой вашего приложения? Вы всегда будете подвержены риску того, что небольшая и очевидная проблема превратится в сложную проблему, которую будет сложно воспроизвести и исследовать. Уничтожение потока небезопасно, поскольку оно может оставить ваше приложение в нескольких разных несовместимых состояниях. Посмотрите информацию по ссылке на cert.org.
ZarathustrA

У нас есть контрольный поток, который при определенных условиях может решить, что весь расчет просто стал бессмысленным. Множество различных исполняемых файлов, выполняющих реальную работу, должны быть забрызганы каким-то вариантом 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, если компилятор оптимизирует работу с локальным хранилищем потоков.
Clinux

1

«Killing a thread» - не правильная фраза для использования. Вот один способ, которым мы можем реализовать изящное завершение / завершение потока по желанию:

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

    }

}

В переменную shouldStop следует добавить ключевое слово volatile, если компилятор оптимизирует локальное хранилище потока.
Алексей Кислицын

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. Когда задача не отвечает на сигнал прерывания. Предположим, что задача выполняет ввод-вывод сокета, который не реагирует на сигнал прерывания, и, следовательно, использование вышеуказанного подхода не прервет задачу, в будущем произойдет тайм-аут, но отмена в блоке 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);
                }
            }

        };
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.