Fin*_*arr 8 java int concurrency
是的,私有成员变量bar
应该是final
对的吗?但实际上,在这种情况下,简单地读取a的值是一个原子操作int
.这在技术上是线程安全吗?
class Foo {
private int bar;
public Foo(int bar) {
this.bar = bar;
}
public int getBar() {
return bar;
}
}
Run Code Online (Sandbox Code Playgroud)
//假设无数个线程重复调用getBar
同一个实例Foo
.
编辑:
假设这是Foo
该类的所有代码; 任何引用Foo
实例的线程都无法更改bar
(不使用反射等长度)
最后的更新:所以我的第一个结论恰好是正确的,只是我的推理是错误的:-(我重新编辑了我的答案,使其有些连贯,而不是隐藏我早先的错误的痕迹.
正如@Wyzard指出的那样,即使bar
在施工后没有办法改变,Foo
仍然不是线程安全的.问题不是原子性,而是可见性.如果线程1正在改变的值bar
在构造函数(从0它的默认值),也不能保证当其他线程将能看到新的值(或者他们是否看到它在所有).
所以foo
看起来像一个不可变的对象.引用Java Concurrency in Practice,第3.4节:
如果对象是不可变的,则:
- 施工后其状态不能改变;
- 它的所有领域都是最终的; 和
- 它是正确构造的(该参考在施工期间不会逃脱).
Foo
1)和3)看起来不错,但不是2).由于上面的推理,这是一个关键点.声明变量final
是确保其在不同线程之间可见性的一种方法.其他方法是声明bar
volatile
或同步其访问方法.但是,当然,在不可变对象的情况下,这些都没有多大意义.
那么为什么final
田地能保证可见度?Java Concurrency in Practice,第3.5.2节的回答:
由于不可变对象非常重要,因此JavaMemory模型为共享不可变对象提供了初始化安全性的特殊保证.正如我们所见,对象引用变得对另一个线程可见并不一定意味着该对象的状态对于消费线程是可见的.为了保证对象状态的一致视图,需要同步.
另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可变对象.为保证初始化安全性,必须满足所有对不变性的要求:不可修复的状态,所有字段都是最终的,以及正确的构造.[...]
任何线程都可以安全地使用不可变对象而无需额外的同步,即使不使用同步来发布它们也是如此.
此保证扩展到正确构造的对象的所有最终字段的值; 无需额外同步即可安全访问最终字段.但是,如果final字段引用可变对象,则仍需要同步来访问它们引用的对象的状态.
如果该领域不是最终的,会发生什么?其他线程可能会默默地看到该字段的陈旧值.没有例外或任何形式的警告 - 这就是为什么这些类型的错误难以追踪的原因之一.
归档时间: |
|
查看次数: |
618 次 |
最近记录: |