转自: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. asp.net core 如何在Controller获取配置文件的值

    场景:我们会把一些配置信息,写在配置文件文件中,便于我们修改和配置.在之前的asp.net 中可以通过ConfigurationManger来获取web.config里面的配置.在.net core ...

  2. Android广播接收器BroadcastRceiver

    一.使用BroadcastRceiver 1.创建BroadcastRceiver(MyRceiver),重写OnReceiver: public void onReceive(Context con ...

  3. 4种scope方法

    默认作用域,自动加载: default_scope { order(created_at: :desc) } model 调用 find_2时才运行 scope :find_2, ->{ whe ...

  4. Thrift的TBinaryProtocol二进制协议分析

    先上张图,说明一下thrift的二进制协议是什么东东. 报文格式编码: bool类型: 一个字节的类型,两个字节的字段编号,一个字节的值(true:1,false:0). Byte类型: 一个字节的类 ...

  5. 深入理解 JavaScript 变量的作用域和作用域链

    一个变量的作用域(scope)是程序源代码中定义这个变量的区域.简单的说,作用域就是变量与函数的可访问范围.全局变量拥有全局作用域,在JavaScript代码中的任何地方都有定义.局部变量是在函数体内 ...

  6. 关于JavaScript初级的知识点一(持续更新 )

    自己刚开始接触JS这是自己一个多月以来的一些总结和回顾. 一.什么是js? js是一种弱类型的脚本语言,是HTML的3大组成部分之一.HTML标签 CSS样式 JS脚本. 二.js的5种基本数据类型 ...

  7. 在_vimrc中 set noexpandtab python 不起效果

    我ctm,今天配置不让tab转为空格,在_vimrc中set noexpandtab 不起效果. set ts=4也不起效果. 但是在命令行中其效果. 我都不知道咋办了. 问人说我有可能使用的不是那个 ...

  8. js数组中sort排序注意的地方

    var a=[1,2,3,4,5] function sum(a,b) { return a-b } //从小到大 function obj(a,b) { return b-a } //从大到小 a. ...

  9. ORA-00257: archiver error. Connect internal only, until freed.

    早上BA抄送客户的邮件过来,说系统用不了,应用系统报异常Unable to open connection to oracle,Microsoft Provider v2.0.50727.42,既然是 ...

  10. 更新Xcode后插件失效问题

    Xcode更新后插件会失效,这个时候需要给插件的Info.plist文件添加新Xcode的UUID 一.首先找到更新后的Xcode的DVTPlugInCompatibilityUUID: 打开路径: ...