【发布时间】: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 = jdata.i = j 和 更改为return ireturn data.i
现在getI 和setI 方法不会对定义它们的对象的状态做任何事情(并且可能是同步的)。即使现在使用synchronized 关键字也会导致程序终止!有趣的是知道状态实际上正在改变的对象 (Data) 没有同步或内置任何东西。那为什么呢?
[1] 它可能只是表现,因为我不清楚到底发生了什么
【问题讨论】:
-
也许你的主线程在终止之前将其本地缓存刷新到主内存..
-
synchronized also 将本地内存与全局内存同步,以便其他线程可能已进行的写入将按预期进行。将同步放在 only getter 上并不能保证让写入可见。但这也不能保证他们也不会被看到。没有什么能保证他们不会被看到。如果您进行写入,则其他线程总是有可能注意到。
-
让我把扳手扔进去。如果您简单地将
System.out.print("");添加到while循环,该应用程序也将工作(即终止)!没有 volatile 和同步的 getter。 -
@KlitosKyriacou,好点,但我认为在该循环中执行
io将违背实验的目的——即使用独立线程的最小设置。print是非常繁重的操作 - 操作中发生的一切将变得难以分析。 -
@inquisitive 我认为 Kitos 在内部谈论的是
System.out.println是synchronzied。
标签: java multithreading synchronized volatile