Search code examples
javamultithreadingjavafxwaitnotify

Wait & Notify among different Classes method


Though my actual problem is bit different , solving the following problem will help me . I have built a simple javafx application with scenebuilder . I would like to take input from user via a textField in my application and print that one in another class Say, class A. How can i make a thread, in class A, wait until my another guicontroller notify after getting value from user . I want to do this in a loop.

To be precise, How can i use wait and notify among threads in different classes ?

Please pardon me for my scrappy question . thnx in advance

Edited: My actual program is to build a chat messenger . Where i have a thread that will , after having the input from user , will forward to recipient . User will give input in a textField which will be handled in a guicontroller.

I want my guiController to notify the Thread that is waiting for sending message.

How can i do that ?


Solution

  • You could theoretically do it like this

    @Override
    public void start(Stage primaryStage) {
        TextField textField = new TextField();
        List<String> messages = new LinkedList<>();
        Thread thread = new Thread(() -> {
            while (true) {
                String message;
                synchronized (messages) {
                    if (messages.isEmpty()) {
                        // empty message queue -> wait
                        try {
                            messages.wait();
                        } catch (InterruptedException ex) {
                            // unknown cause of interrupt -> just try reading the messages anew
                            continue;
                        }
                    }
                    message = messages.remove(0);
                }
                System.out.println("sending message: " + message);
    
                try {
                    // simulate delay
                    Thread.sleep(3000);
                } catch (InterruptedException ex) {
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    
        textField.setOnAction(evt -> {
            synchronized (messages) {
                // add new message to queue
                messages.add(textField.getText());
    
                // notify thread
                messages.notify();
            }
            textField.clear();
        });
    
        Scene scene = new Scene(textField);
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    

    However there are much easier ways to achieve this effect:

    BlockingQueue

    This class is designed for this exact scenario: a consumer waiting for a producer, if necessary.

    LinkedBlockingQueue<String> messages = new LinkedBlockingQueue<>();
    Thread thread = new Thread(() -> {
        while (true) {
            String message;
            try {
                message = messages.take();
            } catch (InterruptedException ex) {
                continue;
            }
            System.out.println("sending message: "+message);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException ex) {
            }
        }
    });
    thread.setDaemon(true);
    thread.start();
    
    textField.setOnAction(evt -> {
        try {
            messages.put(textField.getText());
            textField.clear();
        } catch (InterruptedException ex) {
        }
        
    });
    

    Using a ExecutorService

    This class simply allows you to post the task to send the info to a ExecutorService that takes care of scheduling and executing the task. You can simply submit one task per message.

    private ExecutorService executor;
    
    @Override
    public void init() throws Exception {
        executor = Executors.newSingleThreadExecutor();
    }
    
    @Override
    public void stop() throws Exception {
        executor.shutdownNow();
        // TODO: handle unsent messages or replace with executor.shutdown()
    }
    
    private void postMessage(String message) {
        executor.submit(() -> {
            System.out.println("sending message: "+message);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException ex) {
            }
        });
    }
    
    @Override
    public void start(Stage primaryStage) {
        TextField textField = new TextField();
    
        textField.setOnAction(evt -> {
            postMessage(textField.getText());
            textField.clear();
        });
    
        Scene scene = new Scene(textField);
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    

    Note that there are different executors available to adjust the scheduling and for using multiple threads.