欢迎访问悦橙教程(wld5.com),关注java教程。悦橙教程  java问答|  每日更新
页面导航 : > > 文章正文

如何正确的使用Java事件通知(1)(2)

来源: javaer 分享于  点击 6614 次 点评:287

并发修改

像上面那样写 StateHolder 很容易遇到并发修改异常(ConcurrentModificationException),即使仅仅限制在一个单线程里面用也不例外。但究竟是谁导致了这个异常,它又为什么会发生呢?

  1. java.util.ConcurrentModificationException 
  2.  
  3. at java.util.HashMap$HashIterator.nextNode(HashMap.java:1429
  4.  
  5. at java.util.HashMap$KeyIterator.next(HashMap.java:1453
  6.  
  7. at com.codeaffine.events.StateProvider.broadcast(StateProvider.java:60
  8.  
  9. at com.codeaffine.events.StateProvider.setState(StateProvider.java:55
  10.  
  11. at com.codeaffine.events.StateProvider.main(StateProvider.java:122

乍一看这个错误堆栈包含的信息,异常是由我们用到的一个 HashMap 的 Iterator 抛出的,可在我们的代码里没有用到任何的迭代器,不是吗?好吧,其实我们用到了。要知道,写在 broadcast 方法里的 for each 结构,实际上在编译时是会被转变成一个迭代循环的。

因为在事件广播过程中,如果一个监听器试图从 StateHolder 实例里面把自己移除,就有可能导致 ConcurrentModificationException。所以比起在原先的数据结构上进行操作,有一个解决办法就是我们可以在这组监听器的快照(snapshot)上进行迭代循环。

这样一来,“移除监听器”这一操作就不会再干扰事件广播机制了(但要注意的是通知还是会有轻微的语义变化,因为当 broadcast 方法被执行的时候,这样的移除操作并不会被快照体现出来):

  1. private void broadcast( StateEvent stateEvent ) { 
  2.  
  3. Set snapshot = new HashSet<>( listeners ); 
  4.  
  5. for( StateListener listener : snapshot ) { 
  6.  
  7. listener.stateChanged( stateEvent ); 
  8.  
  9.  

但是,如果 StateHolder 被用在一个多线程的环境里呢?




相关栏目:

用户点评