简体   繁体   中英

How synchronization helps in variable visibility?

public class NoVisibility {
   private static boolean ready;
   private static int number;
   private static class ReaderThread extends Thread {
      public void run() {
         while (!ready)
            Thread.yield();
         System.out.println(number);
      }
   }

   public static void main(String[] args) {
       new ReaderThread().start();
       number = 42;
       ready = true;
   }
}

According to "Java Concurrency in Practice", it may be possible that it will print 0 as write to ready might be made visible to the reader thread before write to a number or program never terminate at all because it does not use adequate synchronization. It is not guaranteed that values of ready and number written by main thread will be visible to reader thread.

How is it possible? Program will be run sequentially by a thread and it first writes to number and then ready variable. Isn't it? And how can this program could loop forever?

There are no guarantees that by changing a variable in one thread, other threads will see the results of those changes. Technically there is no happens-before relation between them and thus no guarantees (whilst in practice you'll see the changes almost all the time).

That's why the thread may run forever.

Secondly, why sometimes 0 ?

Well the JLS says that

Writes in one thread that are in a data race with reads in another thread may, for example, appear to occur out of order to those reads.

Which means that your

number = 42;
ready = true;

Can occur in any order. Now it's more likely they'll appear in order, but again there's no guarantees.

You could fix it by changing the variables to be volatile, in which case the writes will always be visible to the readers or by making the code where you mutate state a critical section (see the book). In general I feel using too many volatile variables is a bit of a hack, so you should try and use them sparingly, for things like thread "running" variables.

public class NoVisibility {
    private static volatile boolean ready;
    private static volatile int number;

    private static class ReaderThread extends Thread {
        public void run() {
            while (!ready)
                Thread.yield();
            System.out.println(number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        number = 42;
        ready = true;
    }
}

If ready is not marked as 'volatile' than the ReaderThread might check its value (as 0). It later does not check again about ready's value because it assume it has not changed.

The volatile keyword instructs the compiler not to have such assumptions at all and that the variable's value might change under his feet.

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