На данный момент в Java принят уведомительный порядок остановки потока (хотя JDK 1.0 и имеет несколько управляющих выполнением потока методов, например stop()
, suspend()
и resume()
— в следующих версиях JDK все они были помечены как deprecated
из-за потенциальных угроз взаимной блокировки).
Для корректной остановки потока можно использовать метод класса Thread
— interrupt()
. Этот метод выставляет некоторый внутренний флаг-статус прерывания. В дальнейшем состояние этого флага можно проверить с помощью метода isInterrupted()
или Thread.interrupted()
(для текущего потока). Метод interrupt()
также способен вывести поток из состояния ожидания или спячки. Т.е. если у потока были вызваны методы sleep()
или wait()
– текущее состояние прервется и будет выброшено исключение InterruptedException
. Флаг в этом случае не выставляется.
Схема действия при этом получается следующей:
- Реализовать поток.
- В потоке периодически проводить проверку статуса прерывания через вызов
isInterrupted()
. - Если состояние флага изменилось или было выброшено исключение во время ожидания/спячки, следовательно поток пытаются остановить извне.
- Принять решение – продолжить работу (если по каким-то причинам остановиться невозможно) или освободить заблокированные потоком ресурсы и закончить выполнение.
Возможная проблема, которая присутствует в этом подходе – блокировки на потоковом вводе-выводе. Если поток заблокирован на чтении данных — вызов interrupt()
из этого состояния его не выведет. Решения тут различаются в зависимости от типа источника данных. Если чтение идет из файла – долговременная блокировка крайне маловероятна и тогда можно просто дождаться выхода из метода read()
. Если же чтение каким-то образом связано с сетью – стоит использовать неблокирующий ввод-вывод из Java NIO.
Второй вариант реализации метода остановки (а также и приостановки) – сделать собственный аналог interrupt()
. Т.е. объявить в классе потока флаги – на остановку и/или приостановку и выставлять их путем вызова заранее определённых методов извне. Методика действия при этом остаётся прежней – проверять установку флагов и принимать решения при их изменении. Недостатки такого подхода. Во-первых, потоки в состоянии ожидания таким способом не «оживить». Во-вторых, выставление флага одним потоком совсем не означает, что второй поток тут же его увидит. Для увеличения производительности виртуальная машина использует кеш данных потока, в результате чего обновление переменной у второго потока может произойти через неопределенный промежуток времени (хотя допустимым решением будет объявить переменную-флаг как volatile
).