首页 > 解决方案 > 如何使用 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……前面的代码已经在这样做了。那么我们从同步代码中得到了什么好处..

可能是我遗漏了一些东西,但请指导。谢谢

标签: javamultithreading

解决方案


您以这种方式解决的问题不是线程 B在 A 执行 get后立即执行集合,而是仍然会返回“旧”(嗯,当时在技术上是正确的,但很快就会出错)值。

同步修复的问题是,即使线程 B在线程 A 读取之前写入,A 也可能由于缓存而读取旧值(很可能是 CPU 缓存,但这取决于 JVM 实现)。从非易失性变量的非同步读取可以使用缓存值。换句话说:同步创建了一个读屏障,这意味着“你必须重新读取这个值,即使你的 CPU 缓存中已经有了它”。

请注意,对于这种特定情况,简单地添加volatiletovalue将具有相同的效果,但对于更复杂的访问模式synchronized(或者它在较新的 API 中的等效Lock性)是必要的。


推荐阅读