ConcurrentHashMap的使用技巧

在日常开发中,资源池是经常遇到的场景,一种简单的实现是按需创建一个资源,然后放入map中缓存起来,后续使用这个资源时直接从map中获取.

最简单可靠的实现是利用HashedMap+synchronized(或者Lock)
这种方式无疑是正确的,但锁的粒度较大,高并发时性能不佳

改进的一种典型思路是利用JUC里的并发工具ConcurrentHashMap,降低锁粒度,提高并发性
[url=http://dmy999.com/article/34/correct-use-of-concurrenthashmap]http://dmy999.com/article/34/correct-use-of-concurrenthashmap[/url]里提到了一种实践,主要代码如下

private ConcurrentMap records = new ConcurrentHashMap();

private Record getOrCreate(String id) {
Record rec = records.get(id);
if (rec == null) {
// record does not yet exist
Record newRec = new Record(id);
rec = records.putIfAbsent(id, newRec);
if (rec == null) {
// put succeeded, use new value
rec = newRec;
}
}
return rec;
}

这种方法直接使用ConcurrentMap,且没有外层的锁,
putIfAbsent方法不会重复放入相同key的对象,
ConcurrentMap内部实现也保证了内存可见性.
看起来是基本正确的.而且大部分情况也是可以正常工作的.

但是这种写法有个致命的问题,那就是
Record newRec = new Record(id);
这个地方在并发情况下可能被多次执行.意味着资源被多次创建.
如果这个资源没有什么外部依赖,多次创建倒是没有问题,不过当遇到
依赖外部资源(文件,socket)时,多次创建就会有报错.

要避免这种资源的多次创建,可以使用double checked的方式实现.
虽然double checked由来已久,且有著名的double checked locking问题
关于此问题(请参见[url]http://en.wikipedia.org/wiki/Double-checked_locking[/url]
)的产生主要是java内存模型导致的,但是使用volatile可以避免lock的问题
可能早些时候很多著作里都会提到volatile代价太高,但随着jvm发展(1.5以后),volatile的实现
也变得轻量高效,具体的论述可以参见[url]http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl[/url]
注意页面里划掉的一大段论述

Double checked最早是为了解决单例模式产生的,目前java的单例模式已经有更好的
实现方式,比如static变量(dcl的wiki描述),比如enum单例(参见effective java 2nd)

但是在资源池场景仍然可以使用Double checked保证我们资源仅被创建一次
同时尽量降低同步的代价
代码如下

private ConcurrentMap records = new ConcurrentHashMap();

private Record getOrCreate(String id) {
Record rec = records.get(id);
if (rec == null) {
synchronized (this) {
rec = records.get(id);
if (rec == null) {
// record does not yet exist
Record newRec = new Record(id);
rec = records.putIfAbsent(id, newRec);
if (rec == null) {
// put succeeded, use new value
rec = newRec;
}
}
}
}
return rec;
}


注意此处使用了sychronized,仍然不能使用HashedMap代替ConcurrentHashMap
因为这种写法有多线程同时访问Map的get和put方法的可能,
而HashedMap的get和put方法并发环境下也是会出错的.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值