【问题标题】:Why a synchronized getter work like a volatile read?为什么同步的 getter 像 volatile 读取一样工作?
【发布时间】:2019-06-10 14:48:45
【问题描述】:

此程序不会终止!

public class Main extends Thread {
  private int i = 0;
  private int getI() {return i; }
  private void setI(int j) {i = j; }

  public static void main(String[] args) throws InterruptedException {
    Main main = new Main();
    main.start();

    Thread.sleep(1000);
    main.setI(10);
  }

  public void run() {
    System.out.println("Awaiting...");
    while (getI() == 0) ;
    System.out.println("Done!");
  } 
}

我理解这是因为运行 Awaiting 循环的 CPU 内核总是看到 i 的缓存副本而错过了更新。

我也明白,如果我制作 volatileprivate int i = 0;,那么 while (getI()... 的行为[1] 就好像每次它都在查询主内存一样- 所以它会看到更新后的值,我的程序将终止。

我的问题是:如果我做

synchronized private int getI() {return i; }

效果出奇的好!!程序终止。

我知道synchronized 用于防止两个不同的线程同时进入一个方法 - 但这里只有一个线程进入getI()。那么这是什么魔法呢?

编辑 1

这(同步)保证对象状态的变化对所有线程都是可见的

因此,我没有直接使用私有状态字段i,而是进行了以下更改:

代替 private int i = 0; 我做了 private Data data = new Data();i = j 更改为 data.i = jreturn i 更改为return data.i

现在getIsetI 方法不会对定义它们的对象的状态做任何事情(并且可能是同步的)。即使现在使用synchronized 关键字也会导致程序终止!有趣的是知道状态实际上正在改变的对象 (Data) 没有同步或内置任何东西。那为什么呢?


[1] 它可能只是表现,因为我不清楚到底发生了什么

【问题讨论】:

  • 也许你的主线程在终止之前将其本地缓存刷新到主内存..
  • synchronized also 将本地内存与全局内存同步,以便其他线程可能已进行的写入将按预期进行。将同步放在 only getter 上并不能保证让写入可见。但这也不能保证他们也不会被看到。没有什么能保证他们不会被看到。如果您进行写入,则其他线程总是有可能注意到。
  • 让我把扳手扔进去。如果您简单地将System.out.print(""); 添加到while 循环,该应用程序也将工作(即终止)!没有 volatile 和同步的 getter。
  • @KlitosKyriacou,好点,但我认为在该循环中执行io 将违背实验的目的——即使用独立线程的最小设置。 print非常繁重的操作 - 操作中发生的一切将变得难以分析。
  • @inquisitive 我认为 Kitos 在内部谈论的是 System.out.printlnsynchronzied

标签: java multithreading synchronized volatile


【解决方案1】:

这只是巧合或平台相关或特定 JVM 相关,JLS 不保证。所以,不要依赖它。

【讨论】:

  • 您能否详细说明为什么参数这保证对象状态的更改对所有线程都是可见的。不适用?
  • @inquisitive 因为它是基于当一个同步方法退出时,它会自动与任何后续对同一对象的同步方法调用建立起一个happens-before关系。但是,在这种情况下,只有one 线程正在进入synchronized 块。如果你 synchronized 同时是 getter 和 setter,那么它是有保证的。
  • 您好,我进一步修改了实验,让 getter setter 同步,但这次我将状态变量拉到不同的对象(请参阅修改后的代码)。现在可见性保证不应该适用,对吧?导致同步在与维护状态的对象不同的对象上。但它仍在发生!你能帮我解释一下吗:)
  • @inquisitive 现在可见性保证不应该适用,对吧?不! synchronized 不保证任何特定对象的可见性!如果threadB在threadA退出后进入block,那么就保证threadB可以像threadA一样看到所有的东西!所以如果threadA设置了任何值,那么threadB就可以得到它!你需要先了解一些关于发生之前的事情,然后你才能更容易地理解它们。 stackoverflow.com/questions/11970428/…
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 2011-07-10
  • 2014-03-03
  • 2011-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多