简体   繁体   中英

java when I invoked Thread.sleep(), the data's visibility

Look at this code:

public class VolatileTest {

    private static boolean ready = false;

    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(){
            @Override
            public void run() {
                ready = true;
                System.out.println("t2 thread should stop!");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread t2 = new Thread(){
            @Override
            public void run() {
               while(!ready){
                   System.out.println("invoking..");
               }
                System.out.println("I was finished");
            }
        };

        t1.start();
        t2.start();
    }
}

I think the result of this code maybe:

t2 thread should stop!
invoking..
I was finished

because of in the multithreading, when the t1 modify 'ready' variable to true,then I made t1 sleep. At the moment, I think, to t2 the 'ready' variable is false!!! because t1 thread is not stop, the variable in t1 is invisible in t2.
But in fact.. I test many times. the result is always this:

在此处输入图片说明

Am my idea is wrong?

First of all, despite calling your class VolatileTest , you are not actually using volatile anywhere in your code.

Since the ready variable is not declared as volatile AND you are accessing it without any explicit synchronization, the behavior is not specified . Specifically, the JLS does not say whether the assignment made in thread 1 to the ready variable will be visible within thread 2.

Indeed, there is not even guaranteed that the run() method for thread 1 will be called before the run() method for thread 2.

Now it seems that your code (as written!) is behaving in a way that is consistent with the write of true always being visible immediately. However, there is no guarantee that that "always" is actually always, or that this will be the case on every Java platform.

I would not be surprised if the syscall associated with sleep is triggering memory cache flushing before the second thread is scheduled. That would be sufficient to cause consistent behavior. Moreover, there is likely to be serendipitous synchronization 1 due to the println calls. However, these are not effects you should ever rely on.


1 - Somewhere in the output stream stack for System.out , the println call is likely to synchronize on the stream's shared data structures. Depending on the ordering of the events, this can have the effect of inserting a happens before relationship between the write and read events.

As I mentioned in my comment, there are no guarantees. ("There is no guarantee what value thread t2 will see for ready, because of improper synchronization in your code. It could be true, it could be false. In your case, t2 saw true. That is consistent with "there is no guarantee what value t2 will see")

You can easily get your test to fail by running it multiple times. When I run below code that does your test 100 times, I always get 14-22 "notReadies", so 14-22% of the cases you will not see the change to ready in Thread t2.

public class NonVolatileTest {

    private static boolean ready = false;

    private static volatile int notReadies = 0;

    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 100; i++) {
            ready = false;

            // Copy original Thread 1 code from the OP here

            Thread t2 = new Thread() {
                @Override
                public void run() {
                    if (!ready) {
                        notReadies++;
                    }
                    while (!ready) {
                        System.out.println("invoking..");
                    }
                    System.out.println("I was finished");
                }
            };

            t1.start();
            t2.start();
            // To reduce total test run time, reduce the sleep in t1 to a 
            // more suitable value like "100" instead of "5000".
            t1.join();
            t2.join();
        }
        System.out.println("Notreadies: " + notReadies);
    }
}

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