java - 为什么同步的 getter 像 volatile 读取一样工作?
问题描述
该程序不会终止!
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
并错过更新。
我也明白,如果我这样做,那么它的行为[1]就好像每次它都在查询主内存一样 - 所以它会看到更新的值并且我的程序将终止。volatile
private int i = 0;
while (getI()...
我的问题是:如果我做
synchronized private int getI() {return i; }
它出奇的有效!!程序终止。
我知道这synchronized
用于防止两个不同的线程同时进入一个方法 - 但这里只有一个线程进入getI()
。那么这是什么法术呢?
编辑 1
这(同步)保证对象状态的变化对所有线程都是可见的
因此,我没有直接拥有私有状态字段i
,而是进行了以下更改:
代替I did private int i = 0;
private Data data = new Data();
,更改为i = j
data.i = j
并更改为return i
return data.i
现在getI
andsetI
方法对定义它们的对象的状态没有做任何事情(并且可能是同步的)。即使现在使用synchronized
关键字也会导致程序终止!有趣的是知道状态实际上正在改变的对象 ( Data
) 没有同步或内置任何东西。那么为什么?
[1]它可能只是表现得那样,实际上,真正发生的事情我不清楚
解决方案
这只是巧合或平台相关或特定 JVM 相关,JLS 不保证。所以,不要依赖它。
推荐阅读
- docker - 复制路径的 Docker 组成不占用端口或卷
- html - 在for循环中并排显示html图像
- javascript - 5000个onclick()弹窗转模态的策略
- scala - Scala - 折叠 - 元组列表(长,字符串)到字符串列表到单个字符串
- javascript - 使用 Javascript 检索 AppScript 的 URL
- homebrew - 与电子打包器一起分发 Homebrew 依赖项
- r - 在 R 中计算运行百分比
- maven - IntelliJ IDEA maven 项目是否使用 maven 下载的 jar 来解决依赖关系?
- python - Datetime 的时间戳函数与预期的 UNIX 时间不匹配
- ios - “使用 Twitter 登录”,现在 Twitter 工具包已经消失了