DEV Community

faangmaster
faangmaster

Posted on • Edited 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

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more