说我有一个班级
public class Foo {
public void printHi() {
System.out.print("Hi");
}
}
在某些客户端代码中我执行类似
的操作public static void main() {
Foo foo = new Foo();
(new Thread(() -> {foo.printHi();})).start();
}
并取消调用Thread Start的事先保证。 那么Foo引用可能对于使用它的那个线程是不可见的,或者更糟糕的是,属于该类的方法是不可见的,但是Foo引用是可见的。我不确定方法是如何存储在像字段这样的对象中,但是这假设它只是属于该对象的内存中的某些内容,因此可能存在可见性问题,但我不确定。有人也可以向我解释那部分吗?
我问这个因为Foo是不可变的,而且在JCIP的书中,Goetz说
另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可变对象。为了保证初始化安全性的保持,必须满足所有不可变性的要求:无法改变的状态,所有领域都是最终的,正确的建设“(Goetz,3.5.2)
但是,它没有任何最终字段,所以它是否算作所有字段都是最终字段?既然没有字段=所有字段?
答案 0 :(得分:2)
有不同的方法可以得到相同的答案。
final
,因为没有字段final
。即使在final
中声明了一些非Foo
字段,但printHi()
的调用(不读取对象的状态)也没有潜在的数据竞争。请注意,这仅适用于由Foo
表达式生成的new Foo(…)
的精确实例,因为子类可以覆盖printHi()
以访问共享的可变状态。
重要的是要强调竞争条件是关于共享的可变数据,而不一定是对象。因此,如果printHi()
访问不同类的共享static
变量,即使Foo
实例不可变和/或正确发布,它也可能产生数据争用。如果foo.printHi()
不访问共享可变状态(或仅使用适当的保护),则在另一个线程中调用printHi()
的代码是安全的。
作为Elliott Frisch mentioned,lambda表达式的行为就像一个不可变对象,所以即使没有{em>发生在之前的关系或者不可变性,代码也是安全的。 Thread.start()
(假设之后未修改Foo
实例)。
答案 1 :(得分:1)
foo
必须(有效)final
才能在此处使用。
Foo foo = null; // <-- for example,
foo = new Foo();
(new Thread(() -> {
foo.printHi(); // <-- compiler error
})).start();