DEV Community

faangmaster
faangmaster

Posted on • Updated on

Задача с собеседования на Java программиста: Поток с функциями остановки, приостановки и возобновления

Напишите поток, который можно приостановить, продолжить/возобновить его выполнение и остановить.

Это вопрос на знание нескольких фактов:

  1. Как в Java создать поток

  2. Знание того факта, что Thread.stop(), Thread.suspend() и Thread.resume() не безопасны и deprecated. Их нельзя использовать.

  3. Знание wait-notify механизма в Java

  4. Знание volatile и/или synchronized в Java

  5. Как работает interrupt()

В Java есть встроенные методы Thread.stop(), Thread.suspend() и Thread.resume() методы у класса Thread. И новичку в многопоточности может показаться, что нужно, просто, их и использовать для этих целей. Но если вы так ответите, это будет красным флагом для вас. Интервьюер поймет, что вы не знаете этой темы совсем.

Эти методы в Java deprecated, т.к. они не безопасны. Почему это так можно почитать в официальной документации тут: https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

Кратко:

Thread.stop() — бросает Error: ThreadDeath, это приводит к тому, что поток отпускает все мониторы(локи), которые держал. Если какие-то объекты были защищены этими мониторами, то они могут оказаться в промежуточном/неконсистентном состоянии. При этом другие потоки, не будут знать, что произошла остановка потока при помощи Thread.stop() и некоторые объекты могут быть в промежуточном состоянии. Это приведет к неправильной работе приложения.
Thread.suspend() — заставляет поток перейти в режим ожидания, но если поток держал какие-то мониторы(локи), то он продолжит их держать. Это может привести к dead-lock. Например, если второй поток, который должен разбудить первый тоже должен вначале получить лок на тот же объект. Они будут вечно друг друга ждать.

Как же правильно остановить поток?

Использовать метод interrupt() + в методе run потока проверять флаг isInterruped() и реагировать на InterruptedException, который могут бросить блокирующие операции.

Как можно приостановить и возобновить выполнение потока?

Например, при помощи wait-notify механизма. Смотри мои статьи про методы Object и задачу на wait-notify: Ping-Pong.

В run добавим:

    synchronized (this) {
        while (isSuspended) {
            wait();
        }
    }
Enter fullscreen mode Exit fullscreen mode

И чтобы приостановить:

    isSuspended = true;
Enter fullscreen mode Exit fullscreen mode

Важный момент: isSuspended должна меняться или в synchronized блоке или быть объявленной как volatile для обеспечения visibility. Смотрите более подробно про volatile: https://www.baeldung.com/java-volatile

    volatile boolean isSuspended
Enter fullscreen mode Exit fullscreen mode

И чтобы возобновить работу потока:

    public synchronized void customResume() {
        this.isSuspended = false;
        notifyAll();
    }
Enter fullscreen mode Exit fullscreen mode

Итого, все вместе:

    public class Main {
        public static void main(String[] args) throws InterruptedException {
            ResumableThread t = new ResumableThread();
            t.start();
            Thread.sleep(10000);
            t.customSuspend();
            Thread.sleep(10000);
            t.customResume();
            Thread.sleep(10000);
            t.customTerminate();
        }
    }

    class ResumableThread extends Thread {
        private volatile boolean isSuspended;

        public synchronized void customResume() {
            this.isSuspended = false;
            notifyAll();
        }

        public void customSuspend() {
            this.isSuspended = true;
        }

        public void customTerminate() {
            interrupt();
        }

        public void run() {
            try {
                while (!isInterrupted()) {
                    doWork();
                    synchronized (this) {
                        while (isSuspended) {
                            System.out.println("Going to sleep");
                            wait();
                        }
                    }
                }
                System.out.println("Interrupted");
            } catch (InterruptedException e) {
                System.out.println("Interrupted");
            }
        }

        private void doWork() throws InterruptedException {
            // Что-то очень важное выполняем
            System.out.println("Doing some work");
            Thread.sleep(1000);
        }
    }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)