Як вбити нитку в Java?


374

Як убити java.lang.Threadв Java?


2
до цих пір ви не можете вбити нитку; тому що знищення () ніколи не реалізовується через схильних до мертвих блокувань
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 (), вона поверне вам правду, і вони продовжать додавати до вашої поточної ThreadGroup [], ви можете побачити це за допомогою Thread.currentThread.getThreadGroup (). list (); він надрукує всі потоки, які він має, і ви побачите кілька примірників своєї теми, якщо ви повторите потік.
AZ_

3
Якщо ви працюєте на ПК, то це не проблема, але якщо ви розробляєте програмне забезпечення для мобільних пристроїв (андроїд я це відчував), ви отримаєте 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()працює, хоча Oracle каже, що він не повинен працювати, якщо interrupt()цього не відбувається. Цікаво, як змусити це працювати і уникати використання застарілого методу.
Danny Lo

129

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

Ви просите перервати все, що він робить, використовуючи Thread.interrupt () (javadoc посилання)

Хороше пояснення того, чому тут у javadoc (посилання на техно-техніку java)


@Fredrik Що відбувається з контекстом теми, коли interrupt()викликається метод? Основне питання пов'язане з генерацією журналу для кожного нового потоку.
ABcDexter

3
@ABcDexter Вся справа в тому, що переривання нічого не перебиває, воно просто сигналізує коду в потоці (або коду, який називається потоком), що хтось попросив його перервати все, що він робить. Тоді потік повинен добре зупинити обробку та повернути, як би це було зроблено, як слід (і в цей момент контекст потоку, ймовірно, також відкидається). ОТОХ, якби ви справді змусили зупинити нитку, ваше питання було б справді гарним, а відповідь не визначеною.
Фредрік

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 Тоді вам слід запустити цей сценарій у власній пісочниці. Пісочниця Java захищає апарат від програми, а не частини програми один від одного.
Девід Шварц

@DavidSchwartz ти маєш на увазі, що я просто повинен використовувати іншу платформу, а не Java?
айванго

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

@DavidSchwartz ASFAIK такі інструменти не могли існувати, якщо вони не підтримуються на рівні платформи. Але звичайно завдання можна було б вирішити за допомогою повністю інтерпретованого механізму сценаріїв. Це може рахувати скорочення, як Erlang та інші речі.
айванго

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
Як бічний натяк: Змінна як прапор працює лише тоді, коли потік працює, і він не застряг. Thread.interrupt () повинен звільняти потік із більшості умов очікування (чекати, спати, читати мережу тощо). Тому ви ніколи не повинні ловити InterruptException, щоб зробити цю роботу.
ReneS

12
Це не є надійним; зробити "прапор", volatileщоб переконатися, що він справно працює скрізь. Внутрішній клас не є статичним, тому прапор повинен бути змінною екземпляра. Прапор слід очистити методом аксесуара, щоб можна було виконувати інші операції (наприклад, переривання). Назва "прапор" не є описовою.
erickson

2
Я не отримую річ "while" у методі run. Чи не означає це, що все, що написано методом запуску, буде повторюватися? це не те, що ми хотіли, щоб ця тема була

2
+1 бажає робити (! Thread.currentThread (). IsInteruppted ()) віддається перевазі
Toby

2
Обидва випадки не вдається, коли, наприклад, ви відкриваєте зовнішній процес всередині часу {// відкрити ext процес} і цей процес повісили, тепер жодна нитка не буде перервана, і вона не досягне кінця, щоб перевірити ваше булеве стан, і ви ліво висить ... спробуйте це, наприклад, запустіть консоль python за допомогою java.exec і спробуйте повернути керування без написання виходу, і подивіться, чи є спосіб убити цей процес і вийти .... немає способу вийти з такої ситуації ...
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.
Стівен C

5

Питання досить розпливчасте. Якщо ви мали на увазі «як мені написати програму, щоб нитка перестала працювати, коли я хочу її», то різні інші відповіді повинні бути корисними. Але якщо ви мали на увазі «У мене надзвичайна ситуація із сервером, я не можу перезапустити зараз, і мені просто потрібна певна різьба, щоб померти, будьте що може», тоді вам потрібен інструмент втручання, який відповідає таким інструментам моніторингу, як jstack.

Для цього я створив jkillthread . Дивіться його інструкції щодо використання.


Дякую! Саме те, що я шукав!
Денис Кокорін

4

Звичайно, є випадок, коли ви використовуєте якийсь не зовсім надійний код. (Я особисто це маю, дозволяючи виконувати завантажені скрипти в моєму середовищі Java. Так, дзвінок тривоги безпеки дзвонить скрізь, але це частина програми.) У цьому нещасному випадку, перш за все, ви просто сподіваєтесь, запитуючи сценаріїв сценаріїв поважати якийсь булевий сигнал запуску / не запуску. Ваша єдина гідна безпечна помилка - викликати метод зупинки на потоці, якщо, скажімо, він працює довше, ніж деякий час очікування.

Але це просто "пристойно", і не абсолютно, тому що код може зафіксувати помилку 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.interrupt ().

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

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.
ЗаратустрА

У нас є контрольна нитка, яка за певних умов може вирішити, що весь розрахунок просто став безглуздим. Багато різноманітних бігунів, які виконують фактичну роботу, повинні бути забризані яким-небудь варіантом 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
нестабільне ключове слово слід додати доCheckUpdates, якщо компілятор оптимізує локальну пам’ять потоку.
clinux

1

"Вбити нитку" - це не правильне словосполучення. Ось один із способів ми можемо реалізувати витончене завершення / вихід потоку за заповітом:

Виконання, яке я використав:

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, якщо компілятор оптимізується за допомогою локального сховища потоків.
Олексій Кислицин

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. Якщо завдання не реагує на сигнал переривання. Скажімо, завдання виконує розетку вводу / виводу, яка не реагує на сигнал переривання і, таким чином, використовуючи вищезазначений підхід, не припинить виконання завдання, майбутнє буде вичерпано, але скасування в остаточному блоці не матиме ефекту , нитка буде продовжувати слухати сокет. Ми можемо закрити сокет або викликати метод закриття підключення, якщо він реалізований пулом.
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.