线程通信:如何发出按键被点击的信号?java.lang.IllegalMonitorStateException

线程通信:如何发出按键被点击的信号?java.lang.IllegalMonitorStateException,java,multithreading,javafx,conditional-statements,Java,Multithreading,Javafx,Conditional Statements,我有一个带有文本字段的简单JavaFX阶段。我想做的是:当用户在文本字段中插入字母时,我现在就想打印出来,看看它是否有效。我正在使用一个线程,因为稍后我想扫描一个听写器,看看用户输入的字母是否是字典中单词的一部分 但我得到:java.lang.IllegalMonitorStateException 有什么想法吗?我似乎不理解Condition.await和多线程的整个概念 import javafx.application.Application; import javafx.scene.Sc

我有一个带有文本字段的简单JavaFX阶段。我想做的是:当用户在文本字段中插入字母时,我现在就想打印出来,看看它是否有效。我正在使用一个线程,因为稍后我想扫描一个听写器,看看用户输入的字母是否是字典中单词的一部分

但我得到:java.lang.IllegalMonitorStateException

有什么想法吗?我似乎不理解Condition.await和多线程的整个概念

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DictionaryThreading extends Application {


private static Lock lock = new ReentrantLock();
public static Condition condition = lock.newCondition();

public static void main(String[] args) {
    launch();
}

private static class ScanWords implements Runnable{

    @Override
    public void run() {
        lock.lock();
        try{    
            while(true){
                this.wait();
                System.out.println("clicked");
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally{
            lock.unlock();
        }   
    }

}

@Override
public void start(Stage primaryStage) throws Exception {
    StackPane pane = new StackPane();

    new ScanWords().run();
    TextField tf = new TextField("Please enter a word");
    tf.setOnKeyPressed(e -> {});
    pane.getChildren().add(tf);

    Scene scene = new Scene(pane);
    primaryStage.setScene(scene);
    primaryStage.show();

}

}
等待方法应在同步块内执行:


我不使用JavaFX,但我认为您需要使用EventListener。尝试使用TextListener或InputMethodListener。例如:

StackPane pane = new StackPane();

TextField tf = new TextField("Please enter a word");

tf.addTextListener(e -> System.out.println("Pushed"));

pane.getChildren().add(tf);

Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();

不需要创建一个只等待用户事件的线程。JavaFX框架已经为您提供了这一功能,它是任何UI工具包的基本功能之一。要响应文本字段中文本的更改,只需使用文本字段的文本属性注册更改侦听器:

public void start(Stage primaryStage) throws Exception {
    StackPane pane = new StackPane();

    TextField tf = new TextField("Please enter a word");
    tf.textProperty().addListener((obs, oldText, newText) -> {
        System.out.println("text changed");
    });
    pane.getChildren().add(tf);

    Scene scene = new Scene(pane);
    primaryStage.setScene(scene);
    primaryStage.show();

}
如果为了响应文本更改而需要做的事情需要很长时间,那么应该在文本字段的侦听器中的后台线程中启动该过程。如果您正在搜索大型搜索,您可能希望取消任何现有的搜索,这样就不会导致大量搜索同时运行。提供了所需的功能:

public class SearchService extends Service<List<String>> {

    // modify and access only on FX Application Thread:
    private String searchString ;

    @Override
    protected Task<List<String>> createTask() {
        final String s = searchString ;
        return new Task<List<String>>() {
            @Override
            protected List<String> call() throws Exception {
                List<String> matches = new ArrayList<>();
                // do search for strings matching s
                // be sure to check isCancelled() regularly
                return matches ;
            }
        };
    }

    public String getSearchString() {
        checkThread();
        return searchString ;
    }

    public void setSearchString(String searchString) {
        checkThread();
        this.searchString = searchString ;
    }

    private void checkThread() {
        if (! Platform.isFxApplicationThread()) {
            throw new IllegalStateException("Not on FX Application Thread");
        }
    }
}
那你就可以了

public void start(Stage primaryStage) throws Exception {
    StackPane pane = new StackPane();

    SearchService searchService = new SearchService();

    searchService.setOnSucceeded(e -> {
        List<String> matches = searchService.getValue();
        // do whatever you need with search results...
        // this is called on FX application thread
    });

    TextField tf = new TextField("Please enter a word");
    tf.textProperty().addListener((obs, oldText, newText) -> {
        searchService.cancel();
        searchService.setSearchText(newText);
        searchService.restart();
    });
    pane.getChildren().add(tf);

    Scene scene = new Scene(pane);
    primaryStage.setScene(scene);
    primaryStage.show();

}

我对JavaFX一无所知。但我坚信,要做到这一点,事件中应该有一些概念。然后,当您搜索的文本发生变化时,您将启动一个新的线程。您不需要线程来等待文本中的更改。您需要了解UI是基于事件的。所以说它不科学:文本会告诉你它何时改变。然后你就可以在那件事上开始行动了。UI中的所有内容都是如此。单击按钮->单击事件=该按钮通知订阅者已单击该按钮。文本字段获得焦点。。。。很多事情都会引发事件。正如其他人所指出的,您根本不应该使用线程来观察UI状态的变化。但是,为了将来的参考,您在这里使用的是锁定并错误地等待。等待/通知完全独立于锁定对象,并且;必须通过创建一个条件并使用wait*和signal*方法来监视锁。FWIW我刚刚编写了一个快速测试,用于扫描一个包含350K个单词(表示为a)的字典,绝大多数扫描只需几毫秒。扫描所有单词最坏的情况仍然不到200毫秒。所以,你是否想在这里穿线,这是一个有争议的边界。这里根本不应该使用它!这将防止OP的问题发生,实际上,我相信这是一个X-Y问题。OP询问监视器状态异常。但实际问题是,他做事情的方式不正确。所以他一开始就不应该有这个问题,因为这不是解决问题的正确方法,原因有三。首先,正如已经指出的,最重要的是,没有理由创建等待用户事件的线程:框架已经做到了这一点。其次,它并不能真正解决问题,因为当用户做某事时,您没有展示如何释放等待。第三,即使要实现这个等待线程,也不应该使用低级的等待通知结构,而应该使用更高级的API,如信号量。@Fildor请注意,您的JavaFX链接已经过时了,它们适用于2.x版。从当前版本开始,例如。
public void start(Stage primaryStage) throws Exception {
    StackPane pane = new StackPane();

    SearchService searchService = new SearchService();

    searchService.setOnSucceeded(e -> {
        List<String> matches = searchService.getValue();
        // do whatever you need with search results...
        // this is called on FX application thread
    });

    TextField tf = new TextField("Please enter a word");
    tf.textProperty().addListener((obs, oldText, newText) -> {
        searchService.cancel();
        searchService.setSearchText(newText);
        searchService.restart();
    });
    pane.getChildren().add(tf);

    Scene scene = new Scene(pane);
    primaryStage.setScene(scene);
    primaryStage.show();

}