尽管是可变的,但这在技术上线程安全吗?
是的,私有成员变量 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
的值(无需使用反射等)
Yes, the private member variable bar
should be final
right? But actually, in this instance, it is an atomic operation to simply read the value of an int
. So is this technically thread safe?
class Foo {
private int bar;
public Foo(int bar) {
this.bar = bar;
}
public int getBar() {
return bar;
}
}
// assume infinite number of threads repeatedly calling getBar
on the same instance of Foo
.
EDIT:
Assume that this is all of the code for the Foo
class; any threads with a reference to a Foo
instance will not be able to change the value of bar
(without going to such lengths as using reflection etc.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
最终更新:所以我的第一个结论碰巧是正确的,只是我的推理是错误的:-(我重新编辑了我的答案,使其有些连贯,而不是隐藏我之前错误的痕迹。
结论
正如 @Wyzard 指出的,即使在构造后无法更改
bar
,Foo
仍然不是线程安全的,问题不在于原子性,而在于线程 1 的可见性。正在更改构造函数中bar
的值(默认值 0),无法保证其他线程何时会看到新值(或者它们是否根本看到它) )。所以
foo
看起来像一个不可变的对象,引用自 Java 并发实践。 ,第 3.4 节:Foo
在 1) 和 3) 上看起来不错,但在 2) 上则不然。由于上述推理,这是一个关键点。声明变量final
是确保其在不同线程之间可见性的一种方法。其他方法是声明bar
易失性
,或同步其访问方法。但当然,对于不可变对象,这些都没有多大意义。Final Fields
那么为什么
final
字段可以保证可见性呢? Java并发实践中的回答,第3.5.2节:如果该字段不是最终字段,会发生什么情况?其他线程可能会默默地看到该字段的陈旧值。没有例外或任何类型的警告 - 这就是此类错误如此难以追踪的原因之一。
Final update: so my first conclusion happened to be right, just my reasoning was faulty :-( I re-edited my answer to make it somewhat coherent, not to hide the traces of my earlier blunder.
Conclusion
As @Wyzard pointed out, even though there is no way to change
bar
after construction,Foo
is still not thread safe. The problem is not atomicity but visibility. If thread 1 is changing the value ofbar
in the constructor (from its default value of 0), there is no guarantee when other threads will get to see the new value (or whether they see it at all).So
foo
looks like an immutable object. Quoting from Java Concurrency in Practice, section 3.4:Foo
looks OK on 1) and 3), but not 2). And that is a crucial point, due to the reasoning above. Declaring a variablefinal
is one way of ensuring its visibility between different threads. The other means are declaringbar
volatile
, or synchronizing its access method(s). But of course, in case of an immutable object, neither of these would make much sense.Final Fields
So why do
final
fields guarantee visibility? Answer from Java Concurrency in Practice, section 3.5.2:And what happens if the field is not final? Other threads may silently see a stale value of the field. There is no exception or any kind of warning - that is one reason why these kinds of bugs are so difficult to trace.