Java理论和实践: 安全构造技术 - 编程入门网
细信息。
Java理论和实践: 安全构造技术(2)时间:2011-02-04 Brian Goetz在构造期间,不要公布“this”引用 一种可以将数据争用引入类中的错误是,在构造函数完成之前,使 this 引用暴露给另一个线程。有时这个引用是显式的,(譬如,直接将 this 存储在静态字段或集合),但还有一些时候它可以是隐式的(譬如,当将一个引用公布给构造函数中的非静态内部类的实例时)。构造函数不是一般的方法 ― 它们有特殊的用于初始化安全的语义。在构造函数完成之后,可以认为对象是处于一种可预测和一致的状态,将引用公布给一个还未完成构造的对象是危险的。清单 2 显示了将这类争用条件引入构造函数的示例。这个示例看上去可能没有危害性,但它可以引发严重的并发性问题。 清单 2. 可能发生的数据争用
乍一看, EventListener 类似乎没有危害性。构造函数最后完成的工作是注册侦听器,这会将引用公布给新对象,这时其它线程可能会看到这个引用。但即使不考虑所有 Java 内存模型(JMM)问题(譬如,多个线程所看见同一数据的不一致性以及内存访问重排序的不同),该代码仍然有将还未完成构造的 EventListener 对象暴露给其它线程的危险。考虑清单 3 中这种情况,当创建 EventListener 的一个子类时,会发生什么: 清单 3. 创建 EventListener 的一个子类时,问题产生了
由于 Java 语言规范要求对 super() 的调用应该是子类构造函数中的第一条语句,所以在完成子类字段的初始化之前,还未构造完的事件侦听器已经对事件源进行了注册。现在, list 字段存在数据争用。如果事件侦听器决定从注册调用内发送一个事件,或者我们很不幸,在这不恰当的时刻,一个事件到来了,则会调用 RecordingEventListener.onEvent() ,而这时 list 仍然是 null 值,结果会抛出 NullPointerException 异常。就象 onEvent() 这样的类方法一样,在编码时,应该避免使用还未初始化完的 final 字段。 清单 2 中的问题在于, EventListener 在构造完成之前会向正在构造的对象公布一个引用。虽然看上去 几乎已经完整地构造了对象,所以将 this 传递给事件源好象是安全的,但这种表像是具有欺骗性的。公布来自构造函数内的 this 引用(如清单 2 所示)就象放置了一颗随时会爆炸的定时炸弹。 Java理论和实践: 安全构造技术(3)时间:2011-02-04 Brian Goetz不要隐式地暴露“this”引用 在根本不使用 this 引用情况下,也有可能会造成逃脱的引用问题。非静态内部类维护着其父类的 this 引用的隐式副本,所以创建匿名的内部类实例,并将其传递给从当前线程外部可以看见的对象,会存在与暴露 this 引用本身所具有的所有相同的风险。考虑清单 4,它与清单 2 有同样的根本问题,但这里没有显式地使用 this 引用: 清单 4. 使用匿名内部类时,不正确地公布了“this”
|
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |