Задача с собеседования на Java программиста: Поток с функциями остановки, приостановки и возобновления
Published: 2023-11-25 23:22:14

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

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

 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](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 ](https://telegra.ph/Kakie-metody-klassa-Object-vy-znaete-05-28)и задачу на wait-notify: [Ping-Pong](https://dev.to/faangmaster/zadacha-na-mnoghopotochnost-shaghaiushchii-robot-ili-pingh-pongh-62d).

В run добавим:

```java
    synchronized (this) {
        while (isSuspended) {
            wait();
        }
    }
```

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

```java
    isSuspended = true;
```

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

```java
    volatile boolean isSuspended
```

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

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

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

```java
    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);
        }
    }
```