繁体   English   中英

发布时没有状态的对象是否总是可见的?

[英]Are objects that have no state always visible when published?

说我有一堂课

public class Foo {
    public void printHi() {
       System.out.print("Hi");
    }
}

在某些客户端代码中,我做类似

public static void main() {
     Foo foo = new Foo();
     (new Thread(() -> {foo.printHi();})).start();
}

并取消调用线程启动之前发生的保证。 然后,使用该线程可能看不到Foo引用,或更糟的是,该类的方法不可见,但是Foo引用是可见的。 我不确定方法是如何存储在像字段这样的对象中的,但是这假设它只是属于该对象的内存中的某物,因此它可能存在可见性问题,但我不确定。 有人可以向我解释这部分吗?

我问这个是因为Foo是不可变的,在JCIP书中Goetz说

“另一方面,即使不使用同步来发布对象引用,也可以安全地访问不可更改的对象。为保证初始化安全,必须满足所有不变性要求:不可修改状态,所有领域是最终的,正确的构造” (Goetz,3.5.2)

但是,它没有任何final字段,因此是否算作所有字段都是final的? 由于没有字段=所有字段?

获得相同答案的方法有多种。

  1. 您的对象是不可变的,因为它没有可以修改的状态。
  2. 它的所有字段都是final字段,因为没有字段不是final字段。
  3. 没有可能的竞争条件,因为没有数据在访问时可以修改。
  4. 即使在Foo声明了一些非final字段,也不读取对象状态的printHi()调用也不会引起潜在的数据争用。 请注意,这仅适用于由new Foo(…)表达式生成的Foo确切实例,因为子类可以重写printHi()以访问共享的可变状态。

    必须强调竞争条件是关于共享的可变数据的,不一定是对象。 因此,如果printHi()访问不同类的共享static变量,则即使Foo实例是不可变的和/或正确发布的,它也可能产生数据争用。 如果printHi()不访问共享可变状态(或仅使用适当的防护),则在另一个线程中调用foo.printHi()的代码是安全的。

正如Elliott Frisch提到的那样 ,lambda表达式无论如何都表现得像一个不可变的对象,因此即使没有Thread.start()关系或Foo的不可变性(假设Foo实例之后没有修改)的发生 ,代码也将是安全的。

foo 必须 (有效) final可以在此处使用。

Foo foo = null; // <-- for example,
foo = new Foo();
(new Thread(() -> {
    foo.printHi(); // <-- compiler error
})).start();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM