简体   繁体   中英

Java: wait for boolean to change

I was looking at the current thread: How to create a thread that waits for a boolean variable to become true?

Everyone seemed to disagree with each other and noone exaplined themselves enough so I shall present this in the easiest way. Here is my code as of today.

boolean connecting = false;

public static void main(String[] args) {
    initUI();
    while(true) {
        if(connecting) {
            connecting = false;
            doSomething();
        }
    };
}

initUI() is a mthod which initiates the User Interface. When the user clicks a button in that class called "Connect" it will call it's action listener and set the boolean "connecting" to "true". When it enters the if area it will then reset the boolean to "false" and run the code which I want.

This works, however... It uses far too much CPU.

If I directly set the buttons action listener to the doSomething() method the program jams (and should) as the action listener method needs to finish in order for the button to reset. However the doSomething() has loops (it's a chat program) so it will not return to the original while (which is displayed here in code) until he disconnects.

So my question, is there anyway to "wait" for the boolean to change to true. For example a listener?

ANSWER: Thanks to Joni I implemented a Thread.

The buttons actionListener now includes:

(new connectThread()).start();

And my thread looks like this:

public class connectThread extends Thread {
    public void run() {
        ui.btn.setEnabled(false);
        doSomething();
        ui.btn.setEnabled(true);
    }
}

As for the question title, waiting for a boolean to change, as another person explained an event listener could be set.

Go back to the code where the event listener calls doSomething() , but with one change: start a new thread that runs doSomething() rather than call it directly. You can find a complete example of how to do this in the Java Tutorial: Simple Background Tasks . The SwingWorker API documentation also has an example.

You are on the wrong track - "busy waiting" as you are doing here is almost always the wrong idea, since it just needlessly burns CPU time.

From what I understand you want to react to a button-press without locking up the UI thread. There is no need to have the thread already waiting before that point - just start it up once the button is pushed, as Joni suggests. If you want to ensure that only one button press is processed at a time, you could use a thread pool with a single thread (see SingleThreadExecutor ).

I also want to point out an important mistake in your example code: connecting needs to be made volatile to tell the compiler that the value could change from another thread. As it is, there is no guarantee that your worker thread will ever see the value change, so it could just loop infinitely even if you set connecting to true in another thread.

So my question, is there anyway to "wait" for the boolean to change to true.

You can't do it (efficiently and responsively) with a bare boolean variable. A sleep / test loop is a poor solution because it will either be expensive or non-responsive. If the sleep interval is small you waste CPU, and if you make it larger your application takes a (relatively) long time to notice the state change.

But if the boolean is updated via a method (eg a setter) then you can code the setter to:

You can either put in a Thread.sleep(100); in the loop, so it's not a tight loop like that, which can easily freeze the rest of the threads.

What you can also do is use a wait/notify mechanism. Just have a shared Object, signaller... have the "while(true)" call "signaller.wait()", then, after your other thread sets the flag (it might not need to), it can call "signaller.notifyAll()" which will let the other thread run its next iteration, and see that the flag is set.

In your listener, instead of setting a flag, you could run doSomething() in a new thread. In the example below, ThreadTest is actually the class where you currently have your listener method (I've called it onClick() ).

public class ThreadTest {
    private ExecutorService service;

    public ThreadTest() {
        service = Executors.newFixedThreadPool(10);
    }

    // Button click listener.
    public void onClick() {
        service.submit(new Runnable() {
            @Override
            public void run() {
                doSomething();
            }
        });
    }

    public void doSomething() {}
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM