第一封:Tutika求助
邮件地址:http://cs.oswego.edu/pipermail/concurrency-interest/2006-May/002482.html
2006年5月12日早上06点01分45秒,一位名叫Tutika的网友发出了"求助"邮件:
全文翻译过来,大概就是:
大家好,我想把我一个多线程的项目里面一些HashMap用ConcurrentHashMap替换掉。在HashMap里面我可以放key或者value为null的数据,没有任何毛病。但是ConcurrentHashMap的key和value都不允许为null。
我想知道针对这一问题,有没有比较好的解决方式。需要说明一下的是,在我的应用程序中,对于值为null的value和key是非常难以判断的。
我的解决方案是想包装一下ConcurrentHashMap,当插入null值的时候用其他的对象来代替,取出该对象时再转换为null。但是这个解决方案的问题是在比如keySet(),values()这样的批量操作的方法中,进行对应的转换是非常困难的。
如果有人对于这个问题有解决思路,请告诉我。这将对我非常有用。
翻译结束。
这里我想插个题外话,关于提问的艺术,我觉得Tutika同学的提问方式就很标准。在什么场景下遇到了什么问题,自己尝试的解决方案是什么,请问有没有更好的解决方案?
好好看看下面的图,别一上来就是:有人吗?在吗?
第二封:热心网友
邮件地址:http://cs.oswego.edu/pipermail/concurrency-interest/2006-May/002484.html
Tutika发出"求救"邮件后的1小时20分18秒,就有热心网友Holger回复了他的问题,
我再来翻译一下:
Tutika:我想把我一个多线程的项目里面的一些HashMap用ConcurrentHashMap替换掉。
Holger:在这样做之前,你必须了解到虽然这样的解决方案看起来好像可以解决你的问题,但是它随之可能给你带来意想不到的结果。某些隐藏很深的原因,他们可能会通过诸如ConcurrentModificationException的形式表现出来。最好是解决并发访问的问题,而不是用ConcurrentHashMap来掩盖问题,因为在这个明显的问题被“修复”之后,你很可能会遇到其他的由于并发带来的bug。
Tutika:在hashMap里面我可以放key或者value为null的数据,没有任何毛病。
Holger认为HashMap里面可以存放null是Java Map类的一个严重错误。
Tutika:但是ConcurrentHashMap的key和value都不允许为null。我想知道针对这一问题,有没有人有比较好的方式去解决。
Holger的建议是在调用方加入检查key和value都不能为空的逻辑。如果你们有单元测试,请在测试中包含对这个逻辑的测试。
Tutika:在我的应用程序中,对于值为null的value和key是非常难以判断的。
Holger:这就是使用允许存放null的HashMap所要付出的代价。
Tutika:我想包装一下ConcurrentHashMap,当插入null值的时候用其他的对象来代替,再取出该对象时再转换为null。但是这个解决方案的问题是在比如keySet(),values()这样的批量操作的方法中,进行值转换是非常困难的。
Holger:即使这样,你仍然会遇到这样的问题:首先你需要找到现有Map的构造函数的所有调用方并修复它们。而且这也是不可能的,比如你有可能是从其他地方获取到这个Map的。
Tutika:如果有人对于这个问题有解决思路,请告诉我。这将对我非常有用。
Holger给出了下面两个选择:
1.首先得接受你的程序是有并发问题的,你得找到问题的原因,而不是试图用
ConcurrentHashMap来掩盖问题。这只是一个表明有其他事情不对劲的信号。意味着你得对整个应用程序或受影响的子系统(如果有的话)进行充分的并发分析,也意味着你必须严格的审视你应用程序里面有并发访问的地方。找到之后你可以再使用Collections.synchronizedMap()或者ConcurrentHashMap来解决。
2.用AOP技术来解决你的问题。我已经附加了一个简单的AspectJ MapCheck切面,您可以将其编织到你的应用程序中。在我的示例中是抛出IllegalArgumentExceptions,当然,你可以根据你的场景修改为跳过这次put操作,或者放默认值。你需要非常认真的评估这是否适合你的场景,因为当调用者错误地传了一个空键,你最终可能会用默认键替换值。我给出的切面是要尽早暴露空键/值问题。在你的业务场景下,也许跳过这个操作也是可以接受的。
总之,解决你的问题没有捷径。
翻译结束。
我来总结一下Holger这个哥们说了什么:
1.你这个程序是有并发问题的,仅仅引入ConcurrentHashMap是治标不治本的方法。
2.在HashMap里面允许放值为null的键/值,就是一个错误的设计。
3.你给出的解决方案是不好的。
4.我给你建议就是你得找到有并发问题,但是自己没有控制好的部分。找到问题的根源。
5.或者你用AOP技术来解决你的问题,虽然我不推荐,但是我还是给你写个示例,我这里是抛出异常,你可以根据你的业务场景具体情况具体分析。
6.你这个问题不太好搞,我只能帮到这里了。