最近在设计一个可缓存的类,发现putIfAbsent使用还是有些坑要注意的,总结一下。之前的代码如下
private static ConcurrentHashMap<String, Pattern> compliedPattern = new ConcurrentHashMap<String, Pattern>(); public static Pattern getPattern(String pattern) { return compliedPattern.putIfAbsent(pattern, Pattern.compile(pattern)); }
之前以为这样一行代码就完美了,结果测试每次都返回null,查看api看到文档:
写道
public V putIfAbsent(K key,
V value)
如果指定键已经不再与某个值相关联,则将它与给定值关联。这等价于:
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
除了原子地执行此操作之外。
指定者:
接口 ConcurrentMap<K,V> 中的 putIfAbsent
参数:
key - 与指定值相关联的键
value - 与指定键相关联的值
返回:
以前与指定键相关联的值,如果该键没有映射关系,则返回 null
抛出:
NullPointerException - 如果指定键或值为 null
V value)
如果指定键已经不再与某个值相关联,则将它与给定值关联。这等价于:
if (!map.containsKey(key))
return map.put(key, value);
else
return map.get(key);
除了原子地执行此操作之外。
指定者:
接口 ConcurrentMap<K,V> 中的 putIfAbsent
参数:
key - 与指定值相关联的键
value - 与指定键相关联的值
返回:
以前与指定键相关联的值,如果该键没有映射关系,则返回 null
抛出:
NullPointerException - 如果指定键或值为 null
这个意思是如果key不存在必然返回null,因为put方法返回的value是之前有映射的value。那么改成这样如何?
public static Pattern getPattern(String pattern) { Pattern p = Pattern.compile(pattern); Pattern ret = compliedPattern.putIfAbsent(pattern, p); if (ret == null) { return ret; } else { return p; } }
但这样还搞个毛的缓存啊,每次都会compile了。正确的解法是:
public static Pattern getPattern(String pattern) { Pattern ret = compliedPattern.get(pattern); if (ret == null) { Pattern newPattern = Pattern.compile(pattern); ret = compliedPattern.putIfAbsent(pattern, Pattern.compile(pattern)); if (ret == null) { ret = newPattern; } } return ret; }
参考:
http://stackoverflow.com/questions/3752194/best-practice-to-use-concurrentmaps-putifabsent
http://stackoverflow.com/questions/10743622/concurrenthashmap-avoid-extra-object-creation-with-putifabsent
http://en.wikipedia.org/wiki/Double-checked_locking