Java理论与实践:做个好的(事件)侦听器 - 编程入门网
ppened() 时,会抛出 NullPointerException。这是“检测然后执行” 序列的一个示例 —— 检测是否存在更多元素,如果存在,就取得下一元素 — — 但是在存在并发修改的情况下,检测之后状态可能已经变化。图 1 演示了这 个问题:
图 1. 并发迭代和修改,造成意料之外的失败 这个问题的一个解决方案是在迭代期间持有对 Vector 的锁;另一个方案是 克隆 Vector 或调用它的 toArray() 方法,在每次发生事件时检索它的内容。 所有这两个方法都有性能上的问题:第一个的风险是在迭代期间,会把其他想访 问侦听器列表的线程锁在外面;第二个则要创建临时对象,而且每次事件发生时 都要拷贝列表。 如果用迭代器(Iterator)去遍历侦听器列表,也会有同样的问题,只是表 现略有不同; iterator() 实现不抛出 NullPointerException,它在探测到迭 代开始之后集合发生修改时,会抛出 ConcurrentModificationException。同样 ,也可以通过在迭代期间锁定集合防止这个问题。 java.util.concurrent 中的 CopyOnWriteArrayList 类,能够帮助防止这个 问题。它实现了 List,而且是线程安全的,但是它的迭代器不会抛出 ConcurrentModificationException,遍历期间也不要求额外的锁定。这种特性 组合是通过在每次列表修改时,在内部重新分配并拷贝列表内容而实现的,这样 ,遍历内容的线程不需要处理变化 —— 从它们的角度来说,列表的内容在遍历 期间保持不变。虽然这听起来可能没效率,但是请记住,在多数观察者情况下, 每个组件只有少量侦听器,遍历的数量远远超过插入和删除的数量。所以更快的 迭代可以补偿较慢的变化过程,并提供更好的并发性,因为多个线程可以同时迭 代列表。 Java理论与实践:做个好的(事件)侦听器(2)时间:2010-12-20 IBM Brian Goetz初始化的安全风险 从侦听器的构造函数中登记它很诱惑人,但是这是一个应当避免的诱惑。它 仅会造成“失效侦听器(lapsed listener)的问题(我稍后讨论它),而且还 会造成多个线程安全问题。清单 2 显示了一个看起来没什么害处的同时构造和 登记侦听器的企图。问题是:它造成到对象的“this”引用在对象完全构造完成 之前转义。虽然看起来没什么害处,因为登记是构造函数做的最后一件事,但是 看到的东西是有欺骗性的: 清单 2. 事件侦听器允许“this”引用转义,造成问题 在继承事件侦听器的时候,会出现这种方法的一个风险:这时,子类构造函 数做的任何工作都是在 EventListener 构造函数运行之后进行的,也就是在 EventListener 发布之后,所以会造成争用情况。在某些不幸的时候,清单 3 中的 onEvent 方法会在列表字段还没初始化之前就被调用,从而在取消 final 字段的引用时,会生成非常让人困惑的 NullPointerException 异常: 清单 3. 继承清单 2 的 EventListener 类造成的问题 即使侦听器类是 final 的,不能派生子类 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |