ConcurrentMap.putIfAbsent(key,value)用法实例

本文研究的主要是ConcurrentMap.putIfAbsent(key,value)用法的相关内容,具体如下。

业务上经常会遇到有这种场景,全局维护一个并发的ConcurrentMap, Map的每个Key对应一个对象,这个对象需要只创建一次。如果Map中该key对应的value不存在则创建,否则直接返回。

我们先看一下代码:

public static Locale getInstance(String language, String country,
      String variant) {
    //...
    String key = some_string;
    Locale locale = map.get(key);
    if (locale == null) {
      locale = new Locale(language, country, variant);
      map.put(key, locale);
    }
    return locale;
  }  

这段代码要做的事情是:

  1. 调用 map.get(key) 方法,判断 map 里面是否有该 key 对应的 value (Locale 对象)。
  2. 如果返回 null,表示 map 里面没有要查找的 key-value mapping。new 一个 Locale 对象,并把 new 出来的这个对象与 key 一起放入 map。
  3. 最后返回新创建的 Locale 对象

我们期望每次调用 getInstance 方法时要保证相同的 key 返回同一个 Local 对象引用。这段代码能实现这个需求吗?

答案是:在单线程环境下可以满足要求,但是在多线程环境下会存在线程安全性问题,即不能保证在并发的情况相同的 key 返回同一个 Local 对象引用。

这是因为在上面的代码里存在一个习惯被称为 put-if-absent 的操作 [1],而这个操作存在一个 race condition:

if (locale == null) {
  locale = new Locale(language, country, variant);
  map.put(key, locale);
} 

因为在某个线程做完 locale == null 的判断到真正向 map 里面 put 值这段时间,其他线程可能已经往 map 做了 put 操作,这样再做 put 操作时,同一个 key 对应的 locale 对象被覆盖掉,最终 getInstance 方法返回的同一个 key 的 locale 引用就会出现不一致的情形。所以对 Map 的 put-if-absent 操作是不安全的(thread safty)。

为了解决这个问题,java 5.0 引入了 ConcurrentMap 接口,在这个接口里面 put-if-absent 操作以原子性方法 putIfAbsent(K key, V value) 的形式存在。正如 javadoc 写的那样:

putIfAbsent方法主要是在向ConcurrentHashMap中添加键—值对的时候,它会先判断该键值对是否已经存在。

  • 如果不存在(新的entry),那么会向map中添加该键值对,并返回null。
  • 如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值。

对上面方法进行改造:

public static Locale getInstance(String language, String country,
      String variant) {
    //...
    String key = some_string;
    Locale locale = map.get(key);
    if (locale == null) {
      locale = new Locale(language, country, variant);
      map.putIfAbsent(key, locale);
    }
    return locale;
  }  

这段代码使用了 Map 的 concurrent 形式(ConcurrentMap、ConcurrentHashMap),并简单的使用了语句map.putIfAbsent(key, locale) 。这同样不能保证相同的 key 返回同一个 Locale 对象引用。

这里的错误出在忽视了 putIfAbsent 方法是有返回值的,并且返回值很重要。

所以,使用 putIfAbsent 方法时切记要对返回值进行判断。

public static Locale getInstance(String language, String country,
      String variant) {
    //...
    String key = some_string;
    Locale locale = map.get(key);
    if (locale == null) {
      locale = new Locale(language, country, variant);
      Locale tmp = map.putIfAbsent(key, locale);
      if (tmp != null) {
        locale = tmp;
      }
    }
    return locale;
}  

【实例1】

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Test {
	public static void main(String[] args) {
		//测试一下currentHashMap.putIfAbsent()
		Map<long, String> clientMap = new ConcurrentHashMap<>();
		System.out.println("首先打印空的clientMap");
		System.out.println("clientMap: " + clientMap);
		System.out.println();
		//在空的clientMap中添加一个新的记录
		System.out.println("在空的clientMap中添加一个新的记录");
		System.out.println("添加之前的clientMap: " + clientMap);
		long netId = 1234567L;
		String str1 = "michael";
		String result = clientMap.putIfAbsent(netId, str1);
		System.out.println("添加之后的clientMap: " + clientMap);
		System.out.println("查看返回值result: " + result);
		System.out.println();
		//重复添加
		System.out.println("重复添加上一次的记录");
		System.out.println("添加之前的clientMap: " + clientMap);
		String result2 = clientMap.putIfAbsent(netId, str1);
		System.out.println("添加之后的clientMap: " + clientMap);
		System.out.println("查看返回值result: " + result2);
		System.out.println();
	}
}

总结

以上就是本文关于ConcurrentMap.putIfAbsent(key,value)用法实例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

您可能感兴趣的文章:

  • Java Map 按key排序和按Value排序的实现方法
  • java HashMap通过value反查key的代码示例
(0)

相关推荐

  • Java Map 按key排序和按Value排序的实现方法

    一.理论准备 Map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等. TreeMap:基于红黑树(Red-Black tree)的 NavigableMap 实现,该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法. HashMap的值是没有顺序的,它是按照key的HashCode来实现的,对于这个无序的HashMap我们要怎么来实现排序呢?参照TreeM

  • java HashMap通过value反查key的代码示例

    复制代码 代码如下: import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;public class MapValueGetKey {  public static void main(String[] args) {    Map map = new HashMap<>();    map.put(1,&qu

  • ConcurrentMap.putIfAbsent(key,value)用法实例

    本文研究的主要是ConcurrentMap.putIfAbsent(key,value)用法的相关内容,具体如下. 业务上经常会遇到有这种场景,全局维护一个并发的ConcurrentMap, Map的每个Key对应一个对象,这个对象需要只创建一次.如果Map中该key对应的value不存在则创建,否则直接返回. 我们先看一下代码: public static Locale getInstance(String language, String country, String variant) {

  • Angular中$cacheFactory的作用和用法实例详解

    先说下缓存: 一个缓存就是一个组件,它可以透明地储存数据,以便以后可以更快地服务于请求.多次重复地获取资源可能会导致数据重复,消耗时间.因此缓存适用于变化性不大的一些数据,缓存能够服务的请求越多,整体系统性能就能提升越多. $cacheFactory介绍: $cacheFactory是一个为Angular服务生产缓存对象的服务.要创建一个缓存对象,可以使用$cacheFactory通过一个ID和capacity.其中,ID是一个缓存对象的名称,capacity则是描述缓存键值对的最大数量. 1.

  • C#集合类用法实例代码详解

    下面介绍C#的集合类 1ArrayList using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections; namespace 动态数组ArrayList { class Program { static void Main(string[] args) { ArrayList

  • php的curl封装类用法实例

    本文实例讲述了两个php curl封装类的用法实例,这两个函数可以让我们非常的方便的使用php curl相关函数.分享给大家供大家参考.具体如下: 使用函数之前我们要需要把php curl模块打开(libeay32.dll, ssleay32.dll, php5ts.dll, php_curl.dll) 开启php curl函数库的步骤 1).去掉windows/php.ini 文件里;extension=php_curl.dll前面的; /*用 echo phpinfo();查看php.ini

  • Mysql inner join on的用法实例(必看)

    语法规则 SELECT column_name(s) FROM table_name1 INNER JOIN table_name2 ON table_name1.column_name=table_name2.column_name 先创建两个表,1.用户,2.用户类别 用户表 CREATE TABLE `user` ( `id` int(32) NOT NULL AUTO_INCREMENT, `name` varchar(16) NOT NULL, `kindid` int(32) NOT

  • Android getevent用法实例详解

     Android getevent用法实例详解 最近在测试设备按键的常用命令,感觉这些命令都有的,但就是不知道怎么查找. 翻阅了几篇博文,才发现有一个getevent,就是指这样的命令. 首先需要说明的是getevent命令后面可以带上具体的input设备,列如getevent /dev/iput/event0,这样可以过滤掉一些不用显示的input的设备. 我在之前的使用中,还是有些找不到点子,也是一步一步使用起来的. 首先看-p 选项, -p选项用于输出input设备相关的一些信息,列如,

  • C++ pair的用法实例详解

    C++ pair的用法实例详解 1 pair的应用 pair是将2个数据组合成一个数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存.另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair. pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量. 2 make_pair函数 template pair make_pair(T1 a, T2 b)

  • Python iter()函数用法实例分析

    本文实例讲述了Python iter()函数用法.分享给大家供大家参考,具体如下: python中的迭代器用起来非常灵巧,不仅可以迭代序列,也可以迭代表现出序列行为的对象,例如字典的键.一个文件的行,等等. 迭代器就是有一个next()方法的对象,而不是通过索引来计数.当使用一个循环机制需要下一个项时,调用迭代器的next()方法,迭代完后引发一个StopIteration异常. 但是迭代器只能向后移动.不能回到开始.再次迭代只能创建另一个新的迭代对象. 反序迭代工具:reversed()将返回

  • Python字典循环添加一键多值的用法实例

    循环写入字典key.value.删除指定的键值对: 原文本'jp_url.txt'每行元素以逗号分隔: host_key,product_id,product_name,cont_start,cont_end ah2.zhangyue.com,100002,掌阅,bookId=,&startChapterId ih2.ireader.com,100002,掌阅,bid=,& www.ireader.com,100002,掌阅,&bid=,&cid m.zhangyue.co

  • python模块常用用法实例详解

    1.time模块(※※※※) import time #导入时间模块 print(time.time()) #返回当前时间的时间戳,可用于计算程序运行时间 print(time.localtime()) #返回当地时间的结构化时间格式,参数默认为时间戳 print(time.gmtime) #返回UTC时间的结构化时间格式 print(time.mktime(time.localtime())) #将结构化时间转换为时间戳 print(time.strftime("%Y-%m-%d %X&quo

随机推荐