Управление потоками

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

Класс Thread предоставляет ряд методов для управления потоками, которые мы можем использовать в программах. Рассмотрим ряд этих методов.

Метод sleep

Статический метод Thread.sleep() приостанавливает текущий поток (переводит в спящий режим) на определенный период времени. Этот метод имеет ряд версий:

static void sleep(long millis)
static void sleep(long millis, int nanos)
static void sleep(Duration duration) 

Все три версии в качестве параметра принимают время приостановки потока. В первой версии - это количество миллисекунд, во второй версии дополнительно передается количество наносекунд, которые добавляются к милллисекундам. В третьей версии время передается через объект Duration

Стоит учитывать, что этот метод может сгенерировать исключение типа InterruptedException. Соответственно нам его надо обработать или передать обработку внешнему коду.

Применим первую версию:

class MyTask implements Runnable {

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

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

        System.out.println(name + " works"); 
        
        try{
            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 task = new MyTask();
        new Thread(task, "MyTask").start();

        System.out.println("Main thread finished...");
    }
}

В данном случае в классе MyTask в методе run(), который определяет действия потока, в конструкции try..catch осуществляем задержку на 1000 миллисекунд (1 минуту)

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

Таким образом, когда выполнение дойдет до вызова Thread.sleep(1000) поток приостановит выполнение и возобновит его только через 1 секунду. В итоге мы получим следующий консольный вывод:

Main thread started...
Main thread finished...
MyTask started...
MyTask works
MyTask finished...

Ожидание завершения потока и метод join

При запуске потоков в примерах ранее основной поток (Main thread) завершался до дочернего потока. Как правило, более распространенной ситуацией является случай, когда основной поток завершается самым последним - после всех своих дочерних потоков. Для этого надо применить метод join():

void join()
void join(long millis)
void join(long millis, int nanos)
boolean join(Duration duration)

Этот метод также имеет несколько версий. Но вне зависимости от версии текущий поток будет ожидать завершения потока, для которого вызван метод join(). С помощью дополнительных параметров в метод можно передать время, в течение которго текущий поток будет ожидать будет ожидать завершения потока.

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

class MyTask implements Runnable {

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

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

        System.out.println(name + " works");
        
        try{
            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 task = new MyTask();
        var t = new Thread(task, "MyTask");
        t.start();

        try{
            t.join();  // текущий поток Main thread ожидает завершения потока t
        }
        catch(InterruptedException ex){
            System.out.println(ex.getMessage());
        }
        
        System.out.println("Main thread finished...");
    }
}

Стоит учитывать, что метод join() также генерирует исключение, поэтому его вызов также помещаем в конструкцию try..catch с обработкой исключения InterruptedException.

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

try{
    t.join();  // текущий поток Main thread ожидает завершения потока t
}
catch(InterruptedException ex){
    System.out.println(ex.getMessage());
}

В итоге теперь главный поток завершится после потока MyTask:

Main thread started...
MyTask started...
MyTask works
MyTask finished...
Main thread finished...

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

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

try{
    t.join(500);  // ждем завершения потока t не более 500 миллисекунд
}
catch(InterruptedException ex){
    System.out.println(ex.getMessage());
}

В примере выше поток MyTask выполняется более 1 секунды, тогда как его ожидание длится не более 500 миллисекунд. Поэтому главный поток завершит свою работу, не дожидаясь завершения MyTaskЖ

Main thread started...
MyTask started...
MyTask works
Main thread finished...
MyTask finished...

Ождание группы потоков

Если в программе используется несколько дочерних потоков, и надо, чтобы главный поток (Main thread) завершался после дочерних, то для каждого дочернего потока надо вызвать метод join(). Например:

class MyTask implements Runnable {

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

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

        System.out.println(name + " works");
        
        try{
            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...");

        // определяем и запускаем 4 потока
        Thread[] tasks = new Thread[4];
        for(int i = 0; i < tasks.length; i++) {

            tasks[i] = new Thread(new MyTask(), "MyTask_" + i);
            tasks[i].start();
        }   

        //  ждем завершения 4 потоков
        try{
            
            for(int i = 0; i < tasks.length; i++)  tasks[i].join();
        }
        catch(InterruptedException ex){
            System.out.println(ex.getMessage());
        }
        
        System.out.println("Main thread finished...");
    }
}

Здесь запускаем 4 потока и затем ожидаем их завершения. В итоге главный поток будет завершаться только после завершения всех четырех дочерних потоков:

Main thread started...
MyTask_2 started...
MyTask_1 started...
MyTask_2 works
MyTask_1 works
MyTask_3 started...
MyTask_3 works
MyTask_0 started...
MyTask_0 works
MyTask_1 finished...
MyTask_2 finished...
MyTask_3 finished...
MyTask_0 finished...
Main thread finished...
Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850