如何同步共享变量使这个程序安全?

How to synchronize the shared variables to make this program safe?

我刚刚读了 Java 并发实践这本书,它显示了一个不安全的程序:

public class NoVisibility {
  private static boolean ready;
  private static int number;
  private static class ReaderThread extends Thread {
    public void run() {
      while (!ready)  <=== synchronized ?
        Thread.yield();
      System.out.println(number);
    }
  }
  public static void main(String[] args) {
    new ReaderThread().start();
    number = 42;  <=== synchronized ?
    ready = true;  <=== synchronized ?
  }
}

书中写道:

Synchronization also has another significant, and subtle, aspect: memory visibility. We want not only to prevent one thread from modifying the state of an object when another is using it, but also to ensure that when a thread modifies the state of an object, other threads can actually see the changes that were made. But without synchronization, this may not happen.

所以我明白即使没有并发写入,线程之间共享的数据,比如一个线程写入另一个线程读取,也需要同步以便写入线程发布到读取线。写入线程可以启动另一个线程,该线程将在启动读取线程后循环读取写入线程写入的数据。数据可以共享,也可以不共享。如果数据是多个变量,则可能会共享某些变量而不会共享其他变量,并且共享顺序可能会有所不同。线程之间共享的所有数据都应该同步。 如果下面的程序没有使用 synchronized 块,那么 reader 线程可能看不到 ready true,或者在看到它的数字集后它可能会看到它。未同步的数据对于线程来说将变得陈旧。为避免数据过时,必须在写访问期间和读访问期间对其进行同步。仅同步写访问不会防止数据过时。

我想知道如何使用同步语句使这个示例程序安全。

这将同步变量的读取和写入,因此它们将是可见的,它使用相同的锁(在 FixedVisibility class 上)来访问和更改静态变量。

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

    public static synchronized boolean getReady() {
        return FixedVisibility.ready;
    }

    public static synchronized void setReady(boolean ready) {
        FixedVisibility.ready = ready;
    }

    public static synchronized int getNumber() {
        return FixedVisibility.number;
    }    

    public static synchronized void setNumber(int number) {
        FixedVisibility.number = number;
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        FixedVisibility.setNumber(42); 
        FixedVisibility.setReady(true); 
    }
}