Вопрос или проблема
Впервые работаю с потоками. Проект принимает ввод от пользователя с помощью лямбды, поэтому мне нужно дождаться, пока эта лямбда получит корректные данные, чтобы продолжить выполнение потока. По моим исследованиям, я не нашел хорошего способа сделать это, кроме как использовать 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()
вне синхронизированного блока.
Решение
Чтобы правильно обеспечить синхронизацию между потоками и гарантировать, что поток будет ожидать корректного ввода, вам следует выполнить следующие шаги:
-
Создание объекта для синхронизации: Используйте объект, например,
wait
, для синхронизации потоков. -
Использование синхронизации: Оборачивайте вызовы
wait()
иnotify()
в блокиsynchronized
. -
Условная проверка: Убедитесь, что
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);
}
}
Объяснение
-
Объект для блокировки: Создается объект
lock
, который используется для синхронизации потоков. -
Лямбда-выражение: Лямбда-выражение
handler[0]
вызывается при получении пользовательского ввода. Внутри него происходит проверка условияmessage.equals("true")
. Если условие выполнено, устанавливаемmatches[0]
вtrue
и вызываемlock.notify()
. -
Ожидание: Поток засыпает с помощью
lock.wait()
, покаmatches[0]
не станет истинным. Как только пользователь вводит "true", поток будет разбужен и продолжит выполнение.
Резюме
Данное решение минимизирует использование активного ожидания (например, while (var == null) {}
) и использует более эффективный механизм синхронизации, что делает код более читабельным и производительным. Ваша реализация будет корректно обрабатывать пользовательский ввод, обеспечивая надежную синхронизацию между потоками.