ConcurrentHashMap的putIfAbsent方法详解与应用_元一软件
📅 2026/7/2 16:15:01
👁️ 阅读次数
📝 编程学习
在并发编程中,ConcurrentHashMap是一个常用的线程安全哈希表实现,它提供了多种原子操作方法来简化并发环境下的数据更新。其中,putIfAbsent方法是一个简单而强大的工具,用于在键不存在时插入值,键存在时则不执行任何操作。本文将详细解析putIfAbsent方法的作用、用法、源码逻辑以及与其他相关方法的对比。
putIfAbsent方法的作用
putIfAbsent方法用于在键不存在时原子性地插入值,键存在时则不执行任何操作。这对于需要避免重复插入或初始化的场景非常有用。
方法定义
V putIfAbsent(K key, V value)- 参数:
key:要检查的键。value:若键不存在时插入的值。
- 返回值:
- 返回键之前关联的值(若键不存在则返回
null)。
- 返回键之前关联的值(若键不存在则返回
核心特性
- 原子性:检查键是否存在和插入值的操作是原子的,避免并发冲突。
- 条件插入:仅当键不存在时插入值,否则忽略操作。
- 无函数计算:值需提前创建,可能引发多余对象创建(与
computeIfAbsent对比)。
典型使用场景
简单缓存系统
ConcurrentHashMap<String, Config>configCache=new ConcurrentHashMap<>();public Config getConfig(String configName){Config config=new Config(configName);// 可能创建多余对象 Config existing=configCache.putIfAbsent(configName, config);returnexisting!=null ? existing:config;}避免重复初始化
ConcurrentHashMap<String, List<String>>listCache=new ConcurrentHashMap<>();public List<String>getList(String key){List<String>list=new ArrayList<>();List<String>existing=listCache.putIfAbsent(key, list);returnexisting!=null ? existing:list;}源码逻辑简化
final V putIfAbsent(K key, V value){if(key==null||value==null)throw new NullPointerException();inthash=spread(key.hashCode());for(Node<K,V>[]tab=table;;){Node<K,V>f;int n, i, fh;if(tab==null||(n=tab.length)==0)tab=initTable();elseif((f=tabAt(tab,i=(n-1)&hash))==null){// 键不存在,尝试插入新节点if(casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null)))break;}elseif((fh=f.hash)==MOVED)tab=helpTransfer(tab,f);//协助扩容 else { V oldVal=null;synchronized(f){//锁住当前桶的头节点 if(tabAt(tab,i)==f){//遍历链表或红黑树查找键 for(Node<K,V>e=f;;){ if(e.hash==hash&&((ek=e.key)==key||key.equals(ek))){oldVal=e.val;break;}if((e=e.next)==null)break;}if(oldVal==null){// 键不存在,插入新节点 addNode(tab, hash, key, value, i);}}}if(oldVal!=null)returnoldVal;}}addCount(1L, binCount);returnnull;}与computeIfAbsent对比
| 特性 | putIfAbsent | computeIfAbsent |
|---|---|---|
| 触发条件 | 键不存在 | 键不存在或值为null |
| 值创建时机 | 调用前创建(可能多余) | 函数内按需创建 |
| 函数参数 | 无 | Function<K, V> |
| 适用场景 | 对象创建成本低、简单插入 | 延迟初始化、复杂对象创建 |
最佳实践
避免多余对象创建:若对象创建成本高,优先使用
computeIfAbsent。// 不推荐:可能创建多余对象 Value obj=new Value();map.putIfAbsent(key, obj);// 推荐:按需创建 map.computeIfAbsent(key, k ->new Value());明确返回值含义:返回值
null可能表示键不存在或原值为null,需结合业务逻辑处理。
完整示例
importjava.util.concurrent.ConcurrentHashMap;public class PutIfAbsentDemo{public static void main(String[]args){ConcurrentHashMap<String, Integer>cache=new ConcurrentHashMap<>();// 插入键值对(键不存在) Integer oldValue=cache.putIfAbsent("a",1);System.out.println("Old value for 'a': "+ oldValue);// 输出: null // 尝试插入已存在的键 oldValue=cache.putIfAbsent("a",2);System.out.println("Old value for 'a': "+ oldValue);// 输出:1System.out.println("Current value for 'a': "+ cache.get("a"));// 输出:1}}
编程学习
技术分享
实战经验