转自: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. runtime-对成员变量操作应用之归档和返归档

    为了实现归档和返归档,我们要让被归档对象的类接受NSCoding协议并且实现协议里的两个方法 - (void)encodeWithCoder:(NSCoder *)aCoder; - (nullabl ...

  2. SQL Server基线算法(同比和环比)

    基线介绍 基线为历史数据统计而成的数据,具有参考价值,并利用基线数据与当前值的对比,通过一定的报警机制,形成实时监控架构.SQL Server计数器采用同比和环比两种方式. 同比:可以计算未来一周的基 ...

  3. 设备旋转,创建水平模式布局--Android studio

    1.在项目工具窗口中,右键单击res目录后选择new--Android resource directory菜单项. 2.从资源类型Resource type列表中选择layout,保持Source ...

  4. Ubuntu虚拟机中断后重启网络断接错误解决方案

    因为该死的windows自动更新,所以vmplayer经常会被强制关闭. 但重新启动后,会发生不能连接到网络的情况显示: waiting for the network configuration…… ...

  5. VB编程的键盘控制

    在VB中,很多控件都提供了键盘输入功能(如文本框.组合框等),但它们对键盘的处理较简单,遇到复杂一点的要求即无能为力. 因而VB提供了KeyPress.KeyUp.KeyDown三种键盘事件,当用户按 ...

  6. MySQL 应用优化

    一.使用连接池 二.减少对MySQL的访问 (A) 避免对同一数据做重复检索. (B) 使用查询缓存,MySQL的查询缓存会存储SELECT查询的命令文本和相应的结果. (C) 增加CACHE层 三. ...

  7. 【openStack】Libcloud 如何支持 keystone V3?

    Examples This section includes some examples which show how to use the newly available functionality ...

  8. Python 之旅

    Python2 之旅:   https://funhacks.net/explore-python/ <Python Cookbook>第三版   PYTHON3   http://pyt ...

  9. selenium 3.0 beta2 初体验

    经过漫长的等待,终于迎来了selenium 3.0 从selenium 1.0 至今,差不多有十多年的历史.这个月终于迎来了selenium3.0 那么selenium3.0 为我们带来了什么? 看一 ...

  10. php二进制安全的含义

    PHP里,有string的概念.string里,每个字符的大小为byte(与PHP相比,Java的每个字符为Character,是UTF8字符,C语言的每个字符可以在编译时选择). byte里,有AS ...