转自:http://blog.csdn.net/exceptional_derek/article/details/40384659

先看一段代码:

  1. public class Locale {
  2. private final static Map<String, Locale> map = new HashMap<String,Locale>();
  3. public static Locale getInstance(String language, String country,
  4. String variant) {
  5. //...
  6. String key = some_string;
  7. Locale locale = map.get(key);
  8. if (locale == null) {
  9. locale = new Locale(language, country, variant);
  10. map.put(key, locale);
  11. }
  12. return locale;
  13. }
  14. // ....
  15. }

这段代码要做的事情是:

  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:
  1. if (locale == null) {
  2. locale = new Locale(language, country, variant);
  3. map.put(key, locale);
  4. }
 因为在某个线程做完 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 写的那样:
  1. /**
  2. * If the specified key is not already associated
  3. * with a value, associate it with the given value.
  4. * This is equivalent to
  5. * <pre>
  6. *   if (!map.containsKey(key))
  7. *       return map.put(key, value);
  8. *   else
  9. *       return map.get(key);</pre>
  10. * except that the action is performed atomically.
  11. * .....
  12. */
所以可以使用该方法替代上面代码里的操作。但是,替代的时候很容易犯一个错误。请看下面的代码:
  1. public class Locale implements Cloneable, Serializable {
  2. private final static ConcurrentMap<String, Locale> map = new ConcurrentHashMap<String, Locale>();
  3. public static Locale getInstance(String language, String country,
  4. String variant) {
  5. //...
  6. String key = some_string;
  7. Locale locale = map.get(key);
  8. if (locale == null) {
  9. locale = new Locale(language, country, variant);
  10. map.putIfAbsent(key, locale);
  11. }
  12. return locale;
  13. }
  14. // ....
  15. }
 这段代码使用了 Map 的 concurrent 形式(ConcurrentMap、ConcurrentHashMap),并简单的使用了语句map.putIfAbsent(key, locale) 。这同样不能保证相同的 key 返回同一个 Locale 对象引用。
 
这里的错误出在忽视了 putIfAbsent 方法是有返回值的,并且返回值很重要。依旧看 javadoc:
  1. /**
  2. * @return  the previous value associated with the specified key, or
  3. *         <tt>null</tt> if there was no mapping for the key.
  4. *         (A <tt>null</tt> return can also indicate that the map
  5. *         previously associated <tt>null</tt> with the key,
  6. *         if the implementation supports null values.)
  7. */
“如果(调用该方法时)key-value 已经存在,则返回那个 value 值。如果调用时 map 里没有找到 key 的 mapping,返回一个 null 值”
 
所以,使用 putIfAbsent 方法时切记要对返回值进行判断。如下所示(java.util.Locale 类中的实现代码):
  1. public final class Locale implements Cloneable, Serializable {
  2. // cache to store singleton Locales
  3. private final static ConcurrentHashMap<String, Locale> cache = new ConcurrentHashMap<String, Locale>(32);
  4. static Locale getInstance(String language, String country, String variant) {
  5. if (language== null || country == null || variant == null) {
  6. throw new NullPointerException();
  7. }
  8. StringBuilder sb = new StringBuilder();
  9. sb.append(language).append('_').append(country).append('_').append(variant);
  10. String key = sb.toString();
  11. Locale locale = cache.get(key);
  12. if (locale == null) {
  13. locale = new Locale(language, country, variant);
  14. Locale l = cache.putIfAbsent(key, locale);
  15. if (l != null) {
  16. locale = l;
  17. }
  18. }
  19. return locale;
  20. }
  21. // ....
  22. }
与前段代码相比,增加了对方法返回值的判断:
  1. Locale l = cache.putIfAbsent(key, locale);
  2. if (l != null) {
  3. locale = l;
  4. }
 
这样可以保证并发情况下代码行为的准确性。
 
-------------------------------------------------

深入理解ConcurrentMap.putIfAbsent(key,value) 用法的更多相关文章

  1. ConcurrentMap.putIfAbsent(key,value) 用法讨论

    ConcurrentMap.putIfAbsent(key,value) 用法讨论 http://wxl24life.iteye.com/blog/1746794

  2. binarySearch(int[] a,int fromIndex,int toIndex, int key)的用法

    package com.Summer_0420.cn; import java.util.Arrays; /** * @author Summer * binarySearch(int[] a,int ...

  3. 理解并掌握Promise的用法

    前沿:  Promise在处理异步操作非常有用.项目中,与后端进行数据请求的时候经常要用到Promise.我们可以用promise + xhr进行ajax的封装.也可以使用基于promise封装的请求 ...

  4. R︱高效数据操作——data.table包(实战心得、dplyr对比、key灵活用法、数据合并)

    每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- 由于业务中接触的数据量很大,于是不得不转战开始 ...

  5. 批量插入或更新操作之ON DUPLICATE KEY UPDATE用法

    实际的开发过程中,可能会遇到这样的需求,先判断某一记录是否存在,如果不存在,添加记录,如果存在,则修改数据.在INSERT语句末尾指定ON DUPLICATE KEY UPDATE可以解决这类问题. ...

  6. $key 的用法

    <?php $attr=array("a","b","c","d"); //$key,默认是主键值,$value, ...

  7. ON DUPLICATE KEY UPDATE用法

    INSERT INTO `books ` (`name`,`count`,`num`) VALUES ('windows','1','2'),('','linux','1','3') ON DUPLI ...

  8. MySQL: ON DUPLICATE KEY UPDATE 用法 避免重复插入数据

    INSERT INTO osc_visit_stats(stat_date,type,id,view_count) VALUES (?,?,?,?) ON DUPLICATEKEY UPDATE vi ...

  9. 通过回调函数的理解来进一步理解ajax及其注意的用法

    一,再一次理解回调函数 (function($){ $.fn.shadow = function(opts){ //定义的默认的参数 var defaults = { copies: 5, opaci ...

随机推荐

  1. Lattice Reduction (LLL) 算法C代码实现

    废话不多说,大名鼎鼎的Lenstra-Lenstra-Lovasz(LLL) 算法.实现参考论文:Factoring Polynomials with Rational Coefficients, 作 ...

  2. Linux2.6.11版本:classic RCU的实现

    转载自:http://www.wowotech.net/kernel_synchronization/linux2-6-11-RCU.html 一.前言 无论你愿意或者不愿意,linux kernel ...

  3. 【转载】--仅用 []()+! 就足以实现几乎任意Javascript代码

    最近在做CTF的题目,遇见了一个都是[]()+!这样的文件,于是百度了一下,发现这个博客对这个有解释. G Reader里Dexter同学的分享,来自sla.ckers.org的又一神作 点我测试 G ...

  4. 用Java来写常见的排序算法

    随着校招的临近 算法是校招中很重要的一个部分 总结了常见几种排序算法,各种算法的时间复杂度和空间复杂度大家也需要多了解下 package com.huwei.sort; /** * 各种排序算法 * ...

  5. python2.7 学习笔记--列表的使用

    同其它编程语言一样,python也提供了丰富的数据结构,以方便数据的处理.本文介绍两种最基本的数据集合,列表和元组的使用. 一.列表使用介绍 可以理解为一个有序的序列.其使用方式举例如下: list= ...

  6. AMD电脑装完Winsows10后开机蓝屏,报错代码:cdmsnroot_s.sys

    背景:今天装了个WIN10,电脑配置:联想 IdeaPad   Z485      : AMD   A8处理器      .完成安装后电脑没有问题,安装了驱动程序后将           电脑用360 ...

  7. Xcode will continue when iPad is finished. "Could not find Developer Disk Image"

    1:  Xcode will continue when iPad is finished. 等待进度条读取完成即可: 2: xcode,安装新版本的iOS 的 xcode 支持文件 的路径: /ap ...

  8. 浅谈HTTP中Get与Post的区别

    引用自:http://www.cnblogs.com/hyddd/archive/2009/03/31/1426026.html Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET ...

  9. appium java 环境搭建

    appium是一款open source 移动自动化测试框架,既支持Android 也支持IOS. appium 基于webdriver协议执行测试脚本. 今天,如炒剩饭,介绍一下搭建appium A ...

  10. Android Studio accelerator key(shortcut)& Basic knowledge

    shift + F6 重构(选文件,ok->下面的控制台,do refactor option + return 快速修复 Activity@Extra() Intent: @FragmentA ...