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())/isInterrupted проверять в любых ключевых точках, // где можно обработать прерывание потока if(i%5==1) System.out.println("Hello from a thread! " + i); > 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()); >