【问题标题】:Is this technically thread safe despite being mutable?尽管是可变的,但这在技术上是线程安全的吗?
【发布时间】:2010-05-05 07:46:15
【问题描述】:

是的,私有成员变量bar 应该是final 对吧?但实际上,在这种情况下,简单地读取int 的值是一个原子操作。那么这在技术上是线程安全的吗?

class Foo {
    private int bar;
    public Foo(int bar) {
        this.bar = bar;
    }
    public int getBar() {
        return bar;
    }
}

// 假设无限数量的线程在Foo 的同一实例上重复调用getBar

编辑:

假设这是Foo 类的所有代码;任何引用Foo 实例的线程都无法更改bar 的值(无需使用反射等)

【问题讨论】:

  • 我想强调一下,bar 除了构造函数之外没有setter...您可以更改代码,使类名以大写字母开头。
  • 很好看。当然bar 没有设置器,我要问的问题是是否存在与 N 个线程相关的问题,所有线程都试图同时读取可变值?

标签: java concurrency int


【解决方案1】:

最终更新:所以我的第一个结论碰巧是正确的,只是我的推理是错误的:-(我重新编辑了我的答案以使其有点连贯,而不是隐藏我之前的痕迹大错特错。

结论

正如@Wyzard 指出的那样,即使在构造之后无法更改barFoo 仍然不是线程安全的。问题不是原子性而是可见性。如果线程 1 正在更改构造函数中 bar 的值(从其默认值 0),则无法保证其他线程何时会看到新值(或者他们是否完全看到它 em>)。

所以foo 看起来像一个不可变对象。引自Java Concurrency in Practice,第 3.4 节:

如果满足以下条件,则对象是不可变的:

  • 构造后无法修改其状态;
  • 它的所有字段都是最终的;和
  • 构造正确(this 引用在构造过程中不会转义)。

Foo 在 1) 和 3) 上看起来不错,但不是 2)。由于上述原因,这是一个关键点。声明变量final 是确保其在不同线程之间可见性的一种方法。另一种方法是声明barvolatile,或同步其访问方法。但当然,如果是不可变对象,这些都没有多大意义。

最终字段

那么为什么finalfields 保证可见性?来自 Java Concurrency in Practice 的回答,第 3.5.2 节:

由于不可变对象如此重要,JavaMemory 模型为共享不可变对象提供了初始化安全的特殊保证。正如我们所见,对象引用对另一个线程可见并不一定意味着该对象的状态对消费线程可见。为了保证对象状态的一致视图,需要同步。

另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可变对象。为了保证初始化安全,必须满足所有不变性要求:不可修改的状态,所有字段都是最终的,以及正确的构造。 [...]

任何线程都可以安全地使用不可变对象,无需额外同步,即使不使用同步来发布它们。

此保证扩展到正确构造对象的所有最终字段的值;无需额外同步即可安全访问 final 字段。但是,如果 final 字段引用可变对象,则仍然需要同步才能访问它们所引用的对象的状态。

如果该字段不是 final,会发生什么?其他线程可能会默默地看到该字段的陈旧值。没有任何例外或任何警告 - 这就是为什么这类错误如此难以追踪的原因之一。

【讨论】:

  • 在什么方面它不是线程安全的,在这种情况下会有什么后果?我正在尝试更深入地了解 Java 中的并发机制。我知道可以让这个线程安全的所有不同方法。
  • @Peter,假设与评论一起显示的代码是系统中唯一的代码。修改int 值的唯一方法是通过反射。
  • @Finbarr,构造后修改 int 不是问题。你应该阅读cache coherence。简而言之,在多处理器系统上,当一个处理器写入内存时,除非完成同步,否则其他处理器可能不会立即看到更改。结果是即使在线程 1 完成了构造函数的运行之后,其他线程可能仍然会看到该内存地址的旧内容,从构造函数运行之前开始。
  • 在这种情况下,我真正没有得到的是,变量不是最终的结果是什么?如果两个线程尝试以完全相同的纳秒访问变量,JVM 是否有可能抛出某种异常?
  • @Peter @Wyzard 感谢您提供的信息。这是困扰我一段时间的事情。事实上,这是所有 Java 程序员都应该注意的事情,因为它非常重要。
猜你喜欢
  • 1970-01-01
  • 2013-04-01
  • 1970-01-01
  • 2015-10-13
  • 1970-01-01
  • 1970-01-01
  • 2011-08-03
  • 1970-01-01
相关资源
最近更新 更多