Как заставить поток ожидать лямбду?

Вопрос или проблема

Впервые работаю с потоками. Проект принимает ввод от пользователя с помощью лямбды, поэтому мне нужно дождаться, пока эта лямбда получит корректные данные, чтобы продолжить выполнение потока. По моим исследованиям, я не нашел хорошего способа сделать это, кроме как использовать while (var == null) {}, что не кажется идеальным.

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

public class MRE {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        InputHandler[] handler = new InputHandler[1];
        Thread thread = new Thread(() -> {
            Object wait = new Object();
            boolean[] matches = new boolean[1];
            synchronized (wait) {
                handler[0] = (message) -> {
                    matches[0] = message.equals("true");
                    wait.notify();
                };
                try {
                    wait.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //потоку нужна установленная логическая переменная для выполнения дальнейших действий
        });
        thread.start();
        while (scanner.hasNext()) {
            handler[0].handleMessage(scanner.nextLine());
        }
    }

    interface InputHandler {
        void handleMessage(String message);
    }
}

Кроме IllegalMonitorStateException, который может случиться, если вы вызовете wait.notify() вне блока synchronized, вы сейчас вызываете wait.notify() независимо от результата проверки.

Следующий код позволит потоку продолжить выполнение только после того, как вы получите совпадение:

        Thread thread = new Thread(() -> {
            Object wait = new Object();
            synchronized (wait) {
                handler[0] = (message) -> {
                    synchronized (wait) {
                        if (message.equals("true")) {
                            wait.notify();
                        }
                    }
                };
                try {
                    wait.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            //потоку нужна установленная логическая переменная для выполнения дальнейших действий
        });

wait.notify() будет вызван только когда условие будет true.

Ответ или решение

Вопрос, который вы задаете, связан с многопоточностью в языке Java и использованием лямбда-выражений для ожидания ввода от пользователя. Обычно для управления состоянием потоков используется механизм синхронизации, позволяющий одному потоку ожидать завершения определенных условий, установленных другим потоком. Ваша задача заключается в том, чтобы заставить поток ждать, пока не поступят корректные данные через лямбда-выражение.

Проблема

Ваша исходная реализация подразумевает использование метода wait() и notify() объекта-синхронизатора. Однако, как вы отметили, существует вероятность возникновения исключения IllegalMonitorStateException, если вы попытаетесь вызвать notify() вне синхронизированного блока.

Решение

Чтобы правильно обеспечить синхронизацию между потоками и гарантировать, что поток будет ожидать корректного ввода, вам следует выполнить следующие шаги:

  1. Создание объекта для синхронизации: Используйте объект, например, wait, для синхронизации потоков.

  2. Использование синхронизации: Оборачивайте вызовы wait() и notify() в блоки synchronized.

  3. Условная проверка: Убедитесь, что notify() вызывается только в случае выполнения определённых условий (в вашем случае — корректного ввода).

Обновленный код

Вот обновленная версия вашего кода с учётом вышеуказанных принципов:

import java.util.Scanner;

public class MRE {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        InputHandler[] handler = new InputHandler[1];
        Object lock = new Object(); // Объект для синхронизации
        boolean[] matches = new boolean[1];

        Thread thread = new Thread(() -> {
            // Объявляем лямбда-выражение для обработки входных данных
            synchronized (lock) {
                handler[0] = (message) -> {
                    synchronized (lock) {
                        if (message.equals("true")) {
                            matches[0] = true; // Устанавливаем условие
                            lock.notify(); // Уведомляем поток
                        }
                    }
                };

                try {
                    while (!matches[0]) {
                        lock.wait(); // Ждем, пока условие не станет истинным
                    }
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            // Продолжаем выполнение, зная, что matches[0] установлено в true
            System.out.println("Условие выполнено, продолжаем обработку...");
        });

        thread.start();

        // Обработка пользовательского ввода
        while (scanner.hasNext()) {
            handler[0].handleMessage(scanner.nextLine());
        }
    }

    interface InputHandler {
        void handleMessage(String message);
    }
}

Объяснение

  1. Объект для блокировки: Создается объект lock, который используется для синхронизации потоков.

  2. Лямбда-выражение: Лямбда-выражение handler[0] вызывается при получении пользовательского ввода. Внутри него происходит проверка условия message.equals("true"). Если условие выполнено, устанавливаем matches[0] в true и вызываем lock.notify().

  3. Ожидание: Поток засыпает с помощью lock.wait(), пока matches[0] не станет истинным. Как только пользователь вводит "true", поток будет разбужен и продолжит выполнение.

Резюме

Данное решение минимизирует использование активного ожидания (например, while (var == null) {}) и использует более эффективный механизм синхронизации, что делает код более читабельным и производительным. Ваша реализация будет корректно обрабатывать пользовательский ввод, обеспечивая надежную синхронизацию между потоками.

Оцените материал
Добавить комментарий

Капча загружается...