java - 如何使用 synchronized 关键字避免过时的数据?
问题描述
在《Java Concurrency in Practice》一书中,3.1.1 状态数据部分下,有一段代码
@NotThreadSafe
public class MutableInteger {
private int value;
public int get() { return value; }
public void set(int value) { this.value = value; }
}
这不是线程安全的,因为:
如果一个线程调用 set,其他调用 get 的线程可能会也可能不会看到该更新。
而在 set 和 get 方法上使用同步关键字使其“正确”。如何?
@ThreadSafe
public class SynchronizedInteger {
@GuardedBy("this") private int value;
public synchronized int get() { return value; }
public synchronized void set(int value) { this.value = value; }
}
这里也是,如果值是 0,并且线程 A 调用了 set(2),而线程 B 调用了 get(),B 可能会得到值 0,然后 A 会将其设置为 2……前面的代码已经在这样做了。那么我们从同步代码中得到了什么好处..
可能是我遗漏了一些东西,但请指导。谢谢
解决方案
您以这种方式解决的问题不是线程 B在 A 执行 get后立即执行集合,而是仍然会返回“旧”(嗯,当时在技术上是正确的,但很快就会出错)值。
同步修复的问题是,即使线程 B在线程 A 读取之前写入,A 也可能由于缓存而读取旧值(很可能是 CPU 缓存,但这取决于 JVM 实现)。从非易失性变量的非同步读取可以使用缓存值。换句话说:同步创建了一个读屏障,这意味着“你必须重新读取这个值,即使你的 CPU 缓存中已经有了它”。
请注意,对于这种特定情况,简单地添加volatile
tovalue
将具有相同的效果,但对于更复杂的访问模式synchronized
(或者它在较新的 API 中的等效Lock
性)是必要的。
推荐阅读
- php - 当添加到列表中的对象相同时,是否可以避免在 php 中的方法调用中再次发生?
- android - ML Kit FaceDetectionProcessor 未检测到耳朵标志
- path - Applescript检查文件是否存在于网络中,带有变量的路径
- php - 我可以启动 cmd 并使用 php 执行一些命令吗?
- geometry - 解决 QGIS 中的无效几何
- angular - Ngif 条件数组和输出到子组件不起作用
- javascript - 如何使用 Three.js 和 Collada Loader 更新 .dae 文件中模型的特定部分
- sql - 查找 hive 中上一个时间戳的天数差异
- c# - 为什么将列添加到数据库后我的实体框架上下文没有更新?
- node.js - 找不到合适的模拟服务器来提供带有预定义 url 的 json 响应