Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thread never stops in Java

I am reading Effective Java and in Chapter 10: Concurrency; Item 66: Synchronize access to shared mutable data, there is some code like this:

public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
    // TODO Auto-generated method stub
    System.out.println(stopRequested);
    Thread backgroundThread = new Thread(new Runnable(){

        @Override
        public void run() {
            // TODO Auto-generated method stub
            int i = 0;
            while (!stopRequested){
                i++;
            }

            System.out.println("done");
        }

    });
    backgroundThread.start();
    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
}

}

First, I think the thread should run one second and then stop, since the stopRequested is set to true afterwards. However, the program never stops. It will never print done. The author said

while (!stopRequested)
    i++;

will be transformed into this:

if (!stopRequested)
     while(true)
         i++;

Could someone explain me this?

And another thing I find is that if I change the program to this:

public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
    // TODO Auto-generated method stub
    System.out.println(stopRequested);
    Thread backgroundThread = new Thread(new Runnable(){

        @Override
        public void run() {
            // TODO Auto-generated method stub
            int i = 0;
            while (!stopRequested){
                i++;
                System.out.println(i);
            }

            System.out.println("done");
        }

    });
    backgroundThread.start();
    TimeUnit.SECONDS.sleep(1);
    stopRequested = true;
}

}

The program runs 1 second and stops as expected. What's the difference here?

like image 507
chrisTina Avatar asked Dec 18 '22 19:12

chrisTina


1 Answers

I doubt the author actually said that (exactly).

But the point is that

while (!stopRequested)
    i++;

could behave like

if (!stopRequested)
     while(true)
         i++;

since the Java spec allows the initial value of stopRequested to be cached in a register, or fetched from a (potentially stale) copy in the memory cache. One thread is not guaranteed to read the results of memory writes made by another thread unless there is a formal "happens before" relationship between the write and the subsequent read. In this case, there is no such relationship. That means that it is not specified whether the child thread will see the result of the parent thread's assignment to stopRequested.

As the author of that book would have explained, the solutions include:

  • declaring stopRequested as volatile,
  • making sure that the code that reads and writes stopRequested does so within a synchronized blocks or methods that synchronizes on the same object,
  • uses Lock objects rather than synchronized, or
  • some other concurrency mechanism that satisfies the "happens before" requirement.

Then you ask why your test seemed to work.

That's is explained by the fact that while the child is not guaranteed to see the effect of the parent's assignment, it is also not guaranteed to NOT see it ... either.

Or to put it another way. The Java spec does not say which of the two possibilities will happen.

Now for a specific program, compiled by a specific compiler, run by a specific version of the JVM on specific hardware, you could find that the program behaved one way (or the other way) consistently. Maybe 99.9% of the time. Maybe even 100% of the time. But the same program compiled and run in a different context could behave differently. The JLS says so.


Another explanation for why the two almost identical versions of the program behave differently is that the System.out PrintWriter object is doing some internal synchronization when println is called. That could be giving you a serendipitous "happens before".

like image 74
Stephen C Avatar answered Dec 21 '22 11:12

Stephen C