1. Введение
В этой краткой статье мы рассмотрим остановку потока
в Java, что не так просто, поскольку метод Thread.stop()
устарел.
Как объясняется в этом обновлении от Oracle, stop()
может привести к повреждению отслеживаемых объектов.
2. Использование флага
Начнем с класса, который создает и запускает поток. Эта задача не завершится сама по себе, поэтому нам нужен способ остановить этот поток.
Для этого мы будем использовать атомарный флаг:
public class ControlSubThread implements Runnable {
private Thread worker;
private final AtomicBoolean running = new AtomicBoolean(false);
private int interval;
public ControlSubThread(int sleepInterval) {
interval = sleepInterval;
}
public void start() {
worker = new Thread(this);
worker.start();
}
public void stop() {
running.set(false);
}
public void run() {
running.set(true);
while (running.get()) {
try {
Thread.sleep(interval);
} catch (InterruptedException e){
Thread.currentThread().interrupt();
System.out.println(
"Thread was interrupted, Failed to complete operation");
}
// do something here
}
}
}
Вместо цикла while
, оценивающего константу true
, мы используем AtomicBoolean
, и теперь мы можем запускать/останавливать выполнение, устанавливая его в true/false.
Как объяснялось во введении к атомарным переменным , использование AtomicBoolean
предотвращает конфликты при установке и проверке переменной из разных потоков.
3. Прерывание потока
Что происходит, когда для sleep()
установлен большой интервал или если мы ждем блокировку
, которая может никогда не быть снята?
Мы сталкиваемся с риском блокировки на длительный период или никогда не завершаем чисто.
Мы можем создать прерывание()
для таких ситуаций, давайте добавим в класс несколько методов и новый флаг:
public class ControlSubThread implements Runnable {
private Thread worker;
private AtomicBoolean running = new AtomicBoolean(false);
private int interval;
// ...
public void interrupt() {
running.set(false);
worker.interrupt();
}
boolean isRunning() {
return running.get();
}
boolean isStopped() {
return stopped.get();
}
public void run() {
running.set(true);
stopped.set(false);
while (running.get()) {
try {
Thread.sleep(interval);
} catch (InterruptedException e){
Thread.currentThread().interrupt();
System.out.println(
"Thread was interrupted, Failed to complete operation");
}
// do something
}
stopped.set(true);
}
}
Мы добавили метод interrupt()
, который устанавливает флаг выполнения в false и вызывает метод
interrupt()
рабочего потока .
Если поток спит, когда это вызывается, sleep()
завершится с InterruptedException,
как и любой другой блокирующий вызов.
Это возвращает поток в цикл, и он завершится, так как выполнение
ложно.
4. Вывод
В этом кратком руководстве мы рассмотрели, как использовать атомарную переменную, при необходимости в сочетании с вызовом interrupt(),
для корректного закрытия потока. Это определенно предпочтительнее вызова устаревшего метода stop()
и риска вечной блокировки и повреждения памяти.
Как всегда, полный исходный код доступен на GitHub .