Java Thread seemingly skipping conditional statement

Adam Jarvis :

For a recent library I'm writing, I wrote a thread which loops indefinitely. In this loop, I start with a conditional statement checking a property on the threaded object. However it seems that whatever initial value the property has, will be what it returns even after being updated.

Unless I do some kind of interruption such as Thread.sleep or a print statement.

I'm not really sure how to ask the question unfortunately. Otherwise I would be looking in the Java documentation. I have boiled down the code to a minimal example that explains the problem in simple terms.

public class App {
    public static void main(String[] args) {
        App app = new App();
    }

    class Test implements Runnable {

        public boolean flag = false;

        public void run() {
            while(true) {

                // try {
                //     Thread.sleep(1);
                // } catch (InterruptedException e) {}

                if (this.flag) {
                    System.out.println("True");
                }
            }
        }

    }

    public App() {
        Test t = new Test();
        Thread thread = new Thread(t);
        System.out.println("Starting thread");
        thread.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {}

        t.flag = true;
        System.out.println("New flag value: " + t.flag);
    }
}

Now, I would presume that after we change the value of the flag property on the running thread, we would immediately see the masses of 'True' spitting out to the terminal. However, we don't..

If I un-comment the Thread.sleep lines inside the thread loop, the program works as expected and we see the many lines of 'True' being printed after we change the value in the App object. As an addition, any print method in place of the Thread.sleep also works, but some simple assignment code does not. I assume this is because it is pulled out as un-used code at compile time.

So, my question is really: Why do I have to use some kind of interruption to get the thread to check conditions correctly?

Stephen C :

So, my question is really: Why do I have to use some kind of interruption to get the thread to check conditions correctly?

Well you don't have to. There are at least two ways to implement this particular example without using "interruption".

  • If you declare flag to be volatile, then it will work.
  • It will also work if you declare flag to be private, write synchronized getter and setter methods, and use those for all accesses.

    public class App {
        public static void main(String[] args) {
            App app = new App();
        }
    
        class Test implements Runnable {
            private boolean flag = false;
    
            public synchronized boolean getFlag() {
                return this.flag;
            }
    
            public synchronized void setFlag(boolean flag) {
                return this.flag = flag;
            }
    
            public void run() {
                while(true) {
                    if (this.getFlag()) {   // Must use the getter here too!
                        System.out.println("True");
                    }
                }
            }
        }
    
        public App() {
            Test t = new Test();
            Thread thread = new Thread(t);
            System.out.println("Starting thread");
            thread.start();
    
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
    
            t.setFlag(true);
            System.out.println("New flag value: " + t.getFlag());
    }
    

But why do you need to do this?

Because unless you use either a volatile or synchronized (and you use synchronized correctly) then one thread is not guaranteed to see memory changes made by another thread.

In your example, the child thread does not see the up-to-date value of flag. (It is not that the conditions themselves are incorrect or "don't work". They are actually getting stale inputs. This is "garbage in, garbage out".)

The Java Language Specification sets out precisely the conditions under which one thread is guaranteed to see (previous) writes made by another thread. This part of the spec is called the Java Memory Model, and it is in JLS 17.4. There is a more easy to understand explanation in Java Concurrency in Practice by Brian Goetz et al.

Note that the unexpected behavior could be due to the JIT deciding to keep the flag in a register. It could also be that the JIT compiler has decided it does not need force memory cache write-through, etcetera. (The JIT compiler doesn't want to force write-through on every memory write to every field. That would be a major performance hit on multi-core systems ... which most modern machines are.)


The Java interruption mechanism is yet another way to deal with this. You don't need any synchronization because the method calls that. In addition, interruption will work when the thread you are trying to interrupt is currently waiting or blocked on an interruptible operation; e.g. in an Object::wait call.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325143&siteId=1