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

Последнее обновление: 16.10.2025

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

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

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

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

class MyTask implements Runnable {
      
    private boolean isActive;
      
    void disable(){ isActive=false; }
      
    MyTask(){ isActive = true; }
      
    public void run(){
          
        String taskName = Thread.currentThread().getName();

        System.out.printf("%s started... \n", taskName);

        int counter = 1; // счетчик циклов

        while(isActive){

            System.out.println("Loop " + counter++);

            try{
                Thread.sleep(400);
            }
            catch(InterruptedException _){
                
                System.out.printf("%s interrupted... \n", taskName);
            }
        }
        System.out.printf("%s finished... \n", taskName);
    }
}

Переменная isActive указывает на активность потока: пока эта переменная равна true, поток будет продолжать работать. Но с помощью метода disable() мы можем сбросить состояние этой переменной в false и таким образом завершить поток.

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

class Program{

    public static void main(String[] args) {
         
        System.out.println("Main thread started...");

        // создаем и запускаем поток
        var myTask = new MyTask();
        new Thread(myTask,"MyTask").start();
            
        try{
            Thread.sleep(1100);
                
            myTask.disable();  // завершаем поток
            
            Thread.sleep(1000);
        }
        catch(InterruptedException e){
            System.out.println("Main thread interrupted...");
        }
        System.out.println("Main thread finished...");
    }
}
class MyTask implements Runnable {
      
    private boolean isActive;
      
    void disable(){ isActive=false; }
      
    MyTask(){ isActive = true; }
      
    public void run(){
          
        String taskName = Thread.currentThread().getName();

        System.out.printf("%s started... \n", taskName);

        int counter=1; // счетчик циклов
        while(isActive){
            System.out.println("Loop " + counter++);
            try{
                Thread.sleep(400);
            }
            catch(InterruptedException _){
                
                System.out.printf("%s interrupted... \n", taskName);
            }
        }
        System.out.printf("%s finished... \n", taskName);
    }
}

Итак, вначале запускается дочерний поток:

var myTask = new MyTask();
new Thread(myTask,"MyTask").start();

Затем на 1100 миллисекунд останавливаем главный поток "Main thread" и потом вызываем метод disable(), который переключает в потоке флаг isActive, и дочерний поток завершается.

myTask.disable();  // завершаем поток

Консольный вывод программы:

Main thread started...
MyTask started... 
Loop 1
Loop 2
Loop 3
MyTask finished... 
Main thread finished...

Прерывание потока и метод interrupt

В языке Java нельзя принудительно завершить поток. Однако с помощью метода interrupt() можно направить запроса на прерывание потока. При вызове метода interrupt() для потока устанавливается статус INTERRUPTED.

При выполнении работы с помощью метода isInterrupted() поток может периодически проверять, был ли он прерван, и, если прерван, завершать выполняемую работу:

// пока поток не прерван
while (!Thread.currentThread().isInterrupted()){

    // делаем некоторую работу
}

Однако, если поток заблокирован, он не может проверить статус с помощью вызова isInterrupted(). Такая ситуация, например, возникает, когда метод interrupt() вызывается для потока, который блокируется вызовом sleep() (а также ряд других, например, методом wait()). В этом случае блокирующий вызов (например, sleep()) завершается исключением InterruptedException. Существуют блокирующие вызовы ввода-вывода, которые не могут быть прерваны. Тогда при завершении операции ввода-вывода также генерируется исключение InterruptedException. И в этом случае мы можем в коде потока обрабатывать исключение:

Runnable r = () -> {   // действия потока
    try  {

        // если действия в цикле, в качестве условия проверяем isInterrupted()
        while (!Thread.currentThread().isInterrupted()) {
            
            // выполняемые действия
        }
    }
    catch (InterruptedException ex){

        // обрабатываем исключение прерывания потока
    }

    // какие-то завершающие действия при необходимости
};

Хотя, если поток прерван, он не обязан завершаться - он может продолжать работать. Но гораздо чаще поток просто интерпретирует прерывание как запрос на завершение. Например, проэмулируем данную ситуацию:

class MyTask implements Runnable {

    public void run(){
         
        String name = Thread.currentThread().getName();  // получаем имя текущего потока

        try{
            
            System.out.println(name + " started...");

            while(!Thread.currentThread().isInterrupted()){

                System.out.println(name + " works");

                Thread.sleep(1000); // приостанавливаем поток на 1000 миллисекунд
            }
        }
        catch(InterruptedException ex){

            System.out.println(ex.getMessage());
        }
        System.out.println(name + " finished...");
    }
} 

public class Program {
  
    public static void main(String[] args) {
          
        System.out.println("Main thread started...");

        // определяем поток
        var t = new Thread(new MyTask(), "MyTask");
        // запускаем поток
        t.start();
        
        try{
            // делаем небольшую задержку, чтобы дочерний поток MyTask успел немного отработать
            Thread.sleep(2000);
        }
        catch(InterruptedException ex){
            System.out.println(ex.getMessage());
        }

        // прерываем поток
        t.interrupt();
        
        System.out.println("Main thread finished...");
    }
}

В методе run() класса MyTask в цикле while проверяем результат метода isInterupted():

try{
            
    .........................

    while(!Thread.currentThread().isInterrupted()){

        System.out.println(name + " works");
        Thread.sleep(1000); // приостанавливаем поток на 1000 миллисекунд
    }
}
catch(InterruptedException ex){

    System.out.println(ex.getMessage());
}

В цикле делаем задержку на 1 секунду с помощью Thread.sleep(). Однако если во время этой задержки во вне будет вызван метод interrupt() для этого потока, то мы столкнемся с исключением InterruptedException. И с помощью конструкции try..catch перехватываем и обрабатываем это исключение.

В методе main запускаем поток MyTask, приостанавливаем главный поток, чтобы MyTask успел немного отработать, и далее вызыаем у MyTask метод interrupt():

var t = new Thread(new MyTask(), "MyTask");
t.start();
        
try{
    Thread.sleep(2000);
}
catch(InterruptedException ex){
    System.out.println(ex.getMessage());
}

// прерываем поток
t.interrupt();

В итоге при вызове метода interrupt() во время приостановки MyTask обработает исключение InterruptedException и завершит работу:

Main thread started...
MyTask started...
MyTask works
MyTask works
Main thread finished...
sleep interrupted
MyTask finished...
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850
Morty Proxy This is a proxified and sanitized view of the page, visit original site.