1

我刚刚阅读了 Java Concurrency in Practice 一书,它显示了一个不安全的程序:

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 ?
  }
}

书中写道:

同步还有另一个重要且微妙的方面:内存可见性。我们不仅要防止一个线程在另一个正在使用对象时修改它的状态,还要确保当一个线程修改一个对象的状态时,其他线程可以实际看到所做的更改。但如果没有同步,这可能不会发生。

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

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

4

1 回答 1

3

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

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); 
    }
}
于 2015-08-11T15:56:28.133 回答