Как остановить поток
Перейти к содержимому

Как остановить поток

  • автор:

Thread как остановить поток java

Принудительно останавливать поток крайне не рекомендуется. Соответствующий метод был признан «нежелательным» к использованию ( Документация Oracle ). Проблема в том, что поток может быть остановлен в процессе выполнения операции и приведет к ошибке, которую сложно будет выявить и исправить. Кроме того, внезапная остановка может привести к потере данных.

Вместо принудительной остановки потока необходимо использовать метод оповещения о прекращении работы потока — interrupt() . Данный метод установит флаг прерывания потока (прекращения работы), который можно проверить методом isInterrupted() и указать логику его обработки и завершения работы потока.

Если же поток был заблокирован, находился в ожидании (wait, sleep, join), то будет сброшен флаг прерывания и выброшено исключение InterruptedException . В таком случае, после обработки исключения и для прерывания потока, необходимо заново установить флаг прерывания методом interrupt() .

import java.util.ArrayList; public class ThreadInterruptClass  public static void main(String[] args)  //создаем пул задач для выполнения TasksPool tasksPool = new TasksPool(); Thread thread = new Thread(new Runnable()  @Override public void run()  //проверяем флаг завершения работы потока while(!Thread.currentThread().isInterrupted())  Runnable task = null; try  task = tasksPool.get(); task.run(); > catch (InterruptedException e)  //т.к. исключением был сброшен флаг завершения работы потока, //устанавливаем его снова Thread.currentThread().interrupt(); > > > >); thread.start(); //генерируем задачи в пул задач for (int i = 0; i  5; i++)  tasksPool.put(taskGenerator()); > //Оповещаем о завершении работы потока, после 3 секунд выполнения. try  Thread.sleep(3000); > catch (InterruptedException e)  e.printStackTrace(); > //Если не прервать выполнение потока, работа программы не завершится. thread.interrupt(); > //Метод для генерирования задач. //Выводит сообщение о старте выполнения задачи, ожидает 1 секунду //и выводит сообщение о завершении задачи. public static Runnable taskGenerator()  return new Runnable()  @Override public void run()  System.out.println("Task started: " + this); try  Thread.sleep(1000); > catch (InterruptedException e)  //т.к. исключением был сброшен флаг завершения работы потока, //устанавливаем его снова Thread.currentThread().interrupt(); > System.out.println("Task finished: " + this); > >; > //Класс для хранения пула задач, который выдает задачу, если она есть, //если задачи нет - блокирует выполнения потока, и принимает задачу, //одновременно снимая блокировку с потока static class TasksPool  ArrayListRunnable> tasks = new ArrayList<>(); //метод, который выдает из пула задачу //если задачи нет, блокируем поток public synchronized Runnable get() throws InterruptedException  while (tasks.isEmpty())  wait(); //блокируем выполнение потока > Runnable task = tasks.get(0); tasks.remove(task); return task; > //метод для помещения задачи в пул //и снятия блокировки с потока public synchronized void put(Runnable task)  tasks.add(task); notify(); //снимаем блокировку с потока > > > 

При запуске данного кода будет выполнено 3 задачи, хотя в цикле сгенерируется 5 задач. Все потому, что метод interrupt() будет вызван с 3-х секундной задержкой, поток выполнит текущую задачу (в данном случае 3-ю) и завершится.

Как удалить или остановить поток?

Как удалить или остановить поток, чтобы выполнить определенное действие и запустить (желательно удалить)?

Отслеживать
25.1k 4 4 золотых знака 45 45 серебряных знаков 81 81 бронзовый знак
задан 17 ноя 2015 в 19:56
157 1 1 золотой знак 2 2 серебряных знака 8 8 бронзовых знаков
создайте функцию завершения потока на самом классе потока (destructor)
17 ноя 2015 в 19:58
@Saidolim так себе совет. Поток не должен заниматься менеджментом себя.
17 ноя 2015 в 19:59

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

17 ноя 2015 в 20:13

1 ответ 1

Сортировка: Сброс на вариант по умолчанию

Все потоки нужно прерывать при помощи метода interrupt() .
В самом потоке, который возможно будет прерван — нужно устанавливать проверки isInterrupted() во всех ключевых точках (где это необходимо) и обрабатывать соответственно.
Ну это если в двух словах, а так лучше прочтите что-то по типу этой статьи на Хабре.

import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; public class Main < public static void main(String[] args) < final HelloThread helloThread = new HelloThread(); //final - для таймера helloThread.start(); Timer timer = new Timer(1000, new ActionListener() < @Override public void actionPerformed(ActionEvent event) < System.out.println("Interrupting thread. "); helloThread.interrupt(); >>); timer.setRepeats(false); timer.start(); System.out.println("Thread main still work"); > > class HelloThread extends Thread < public void run() < try < for (int i = 0; i < 1000000; i++) < if (!isInterrupted()) else < throw new InterruptedException(); //бросаем исключение в таких случаях как этот (циклы) >> > catch (InterruptedException e) < System.out.println("Thread is interrupted"); //Закрываем все стримы если есть >//Не забывайте, что поток завершается когда нечего испонять System.out.println("Nothing to say"); //и окончательно он завершится после этой строки > > 

Вообще в java doc не рекомендуют пользоваться deprecated методами (такими как Thread.stop() ). Метод Thread.stop() убивает поток не обрабатывая и самое главное —

поток может быть «убит» во время выполнения операции, обрыв которой на полуслове оставит некоторый объект в неправильном состоянии, что приведет к появлению трудноотлавливаемой и случайным образом возникающей ошибке

(цитата из все той-же статьи на Хабре)

Как остановить поток?

В Java поток представлен классом Thread . В нём есть метод stop() , но пользоваться им нельзя, метод помечен как deprecated. Такая жесткая остановка моментально возвращает все захваченные потоком мониторы, и защищенные ими данные могут оказаться в неконсистентном состоянии.

Разработчики рекомендуют вместо этого использовать флаг, который будет показывать о намерении остановить поток. Флаг выставляется извне потока, а внутри проверяется в подходящий момент. Если нужно остановиться, поток просто выходит из метода run() . В качестве такого флага подойдет переменная типа AtomicBoolean .

Когда в потоке используются блокирующие операции, обычно для определенного типа операции существует свой способ её прервать. Например, можно закрыть сокет, на котором поток ожидает. Для большинства блокирующих операций сработает метод Thread.interrupt() . С его помощью можно прервать Object.wait() и операции из NIO.

Останется только правильно обработать такое прерывание. Прерванный wait() выбросит InterruptedException , Selector.select() вернет результат. Чтобы отличить осознанное прерывание с целью завершить тред от какого-либо другого, его обработку всё ещё необходимо снабдить проверкой флага.

Как остановить поток

Примеры потоков ранее представляли поток как последовательный набор операций. После выполнения последней операции завершался и поток. Однако нередко имеет место и другая организация потока в виде бесконечного цикла. Например, поток сервера в бесконечном цикле прослушивает определенный порт на предмет получения данных. И в этом случае мы также можем предусмотреть механизм завершения потока.

Завершение потока

Распространенный способ завершения потока представляет опрос логической переменной. И если она равна, например, false, то поток завершает бесконечный цикл и заканчивает свое выполнение.

Определим следующий класс потока:

class MyThread implements Runnable < private boolean isActive; void disable()< isActive=false; >MyThread() < isActive = true; >public void run() < System.out.printf("%s started. \n", Thread.currentThread().getName()); int counter=1; // счетчик циклов while(isActive)< System.out.println("Loop " + counter++); try< Thread.sleep(400); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >> System.out.printf("%s finished. \n", Thread.currentThread().getName()); > >

Переменная isActive указывает на активность потока. С помощью метода disable() мы можем сбросить состояние этой переменной.

Теперь используем этот класс:

public static void main(String[] args) < System.out.println("Main thread started. "); MyThread myThread = new MyThread(); new Thread(myThread,"MyThread").start(); try< Thread.sleep(1100); myThread.disable(); Thread.sleep(1000); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.println("Main thread finished. "); >

Итак, вначале запускается дочерний поток: new Thread(myThread,»MyThread»).start() . Затем на 1100 миллисекунд останавливаем Main thread и потом вызываем метод myThread.disable() , который переключает в потоке флаг isActive. И дочерний поток завершается.

Метод interrupt

Еще один способ вызова завершения или прерывания потока представляет метод interrupt() . Вызов этого метода устанавливает у потока статус, что он прерван. Сам метод возвращает true, если поток может быть прерван, в ином случае возвращается false.

При этом сам вызов этого метода НЕ завершает поток, он только устанавливает статус: в частности, метод isInterrupted() класса Thread будет возвращать значение true. Мы можем проверить значение возвращаемое данным методом и прозвести некоторые действия. Например:

class JThread extends Thread < JThread(String name)< super(name); >public void run() < System.out.printf("%s started. \n", Thread.currentThread().getName()); int counter=1; // счетчик циклов while(!isInterrupted())< System.out.println("Loop " + counter++); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); > > public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); JThread t = new JThread("JThread"); t.start(); try< Thread.sleep(150); t.interrupt(); Thread.sleep(150); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.println("Main thread finished. "); > >

В классе, который унаследован от Thread, мы можем получить статус текущего потока с помощью метода isInterrupted() . И пока этот метод возвращает false, мы можем выполнять цикл. А после того, как будет вызван метод interrupt, isInterrupted() возвратит true, и соответственно произойдет выход из цикла.

Возможный консольный вывод:

Main thread started. JThread started. Loop 1 Loop 2 Loop 3 Loop 4 JThread finished. Main thread finished.

Если основная функциональность заключена в классе, который реализует интерфейс Runnable, то там можно проверять статус потока с помощью метода Thread.currentThread().isInterrupted() :

class MyThread implements Runnable < public void run()< System.out.printf("%s started. \n", Thread.currentThread().getName()); int counter=1; // счетчик циклов while(!Thread.currentThread().isInterrupted())< System.out.println("Loop " + counter++); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); > > public class Program < public static void main(String[] args) < System.out.println("Main thread started. "); MyThread myThread = new MyThread(); Thread t = new Thread(myThread,"MyThread"); t.start(); try< Thread.sleep(150); t.interrupt(); Thread.sleep(150); >catch(InterruptedException e) < System.out.println("Thread has been interrupted"); >System.out.println("Main thread finished. "); > >

Однако при получении статуса потока с помощью метода isInterrupted() следует учитывать, что если мы обрабатываем в цикле исключение InterruptedException в блоке catch, то при перехвате исключения статус потока автоматически сбрасывается, и после этого isInterrupted будет возвращать false.

Например, добавим в цикл потока задержку с помощью метода sleep:

public void run() < System.out.printf("%s started. \n", Thread.currentThread().getName()); int counter=1; // счетчик циклов while(!isInterrupted())< System.out.println("Loop " + counter++); try< Thread.sleep(100); >catch(InterruptedException e) < System.out.println(getName() + " has been interrupted"); System.out.println(isInterrupted()); // false interrupt(); // повторно сбрасываем состояние >> System.out.printf("%s finished. \n", Thread.currentThread().getName()); >

Когда поток вызовет метод interrupt, метод sleep сгенерирует исключение InterruptedException, и управление перейдет к блоку catch. Но если мы проверим статус потока, то увидим, что метод isInterrupted возвращает false. Как вариант, в этом случае мы можем повторно прервать текущий поток, опять же вызвав метод interrupt(). Тогда при новой итерации цикла while метода isInterrupted возвратит true, и поизойдет выход из цикла.

Либо мы можем сразу же в блоке catch выйти из цикла с помощью break:

while(!isInterrupted()) < System.out.println("Loop " + counter++); try< Thread.sleep(100); >catch(InterruptedException e) < System.out.println(getName() + " has been interrupted"); break; // выход из цикла >>

Если бесконечный цикл помещен в конструкцию try. catch, то достаточно обработать InterruptedException:

public void run() < System.out.printf("%s started. \n", Thread.currentThread().getName()); int counter=1; // счетчик циклов try< while(!isInterrupted())< System.out.println("Loop " + counter++); Thread.sleep(100); >> catch(InterruptedException e) < System.out.println(getName() + " has been interrupted"); >System.out.printf("%s finished. \n", Thread.currentThread().getName()); >

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *