Java并发编程学习之ConcurrentHashMap如何保证线程安全

Java并发编程学习之ConcurrentHashMap如何保证线程安全

前言

  • 大佬语录

    并发不止并发写冲突,读写也有冲突。并发写有几种解决办法:

    1. 单线程话(a.直接上锁,b.借助队列+异步单线程,如果需要知道结果可能需要回掉等机制)
    2. 双重检测
    3. 借助于volatile,解决写和读的冲突
    4. 借助于其他无锁原子术语

    并发和事务是解决同样的问题:原子性,一致性,隔离性,持久性(也叫可见)。

    乐观锁的思想:就是第一把自己看见的东西保存下来,做完处理以后,然后去原子更新(putIfAbsent,replace),更新成功怎么处理,更新失败(就是别人也在更新)怎么处理(重试,或者返回结果)。

    cas 也是类似的思路,volatile只解决了可见性,它使用的场景,就是单线程写,多线程读的场景

  • 怎么解决并发更新Map集合的问题?

    单个操作安全并不代表整体安全

putIfAbsent

  • 内容介绍

    java 5.0 引入了 ConcurrentMap 接口,在这个接口里面 put-if-absent 操作以原子性方法 putIfAbsent(K key, V value) 的形式存在

  • 方法说明

    V putIfAbsent(K key, V value)

    如果key对应的value不存在,则put进去,返回null。否则不put,返回已存在的value

  • 适用场景

    Map中新增key和value。

  • 错误示例

    使用了 Map 的 concurrent 形式(ConcurrentMap、ConcurrentHashMap),并简单的使用了语句map.putIfAbsent(key, locale) 。这同样不能保证相同的 key 返回同一个 Locale 对象引用。不要忽视了 putIfAbsent 方法是有返回值的,并且返回值很重要

    
    
      public class Locale implements Cloneable, Serializable {  
    
          private final static ConcurrentMap<String, Locale> map = new ConcurrentHashMap<String, Locale>();
    
          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;  
          }  
          // ....  
      }  
    
    

    假设A线程map.get(key)获取为null,则构造新的Locale对象LA,这时由于并发操作,另外个线程B也构造了新的Locale对象LB,然后A先调用putIfAbsent将LA存入map并返回个null,A线程此时获取的是LA正确,但是线程B调用putIfAbsent将LB存入map发现key-LA已经存在,则不进行put操作,返回值为LA,但是实际return locale返回的却是LB,返回不是同一个 Locale 对象引用

    “如果(调用该方法时)key-value 已经存在,则不put并返回那个 value 值。如果调用时 map 里没有找到 key 的 mapping,返回一个 null 值”

  • 正确示例

    
    
      public final class Locale implements Cloneable, Serializable {
    
          // cache to store singleton Locales  
          private final static ConcurrentHashMap<String, Locale> cache = new ConcurrentHashMap<String, Locale>(32);
    
          static Locale getInstance(String language, String country, String variant) {  
    
            if (language== null || country == null || variant == null) {  
                throw new NullPointerException();  
            }  
        
            StringBuilder sb = new StringBuilder();  
            sb.append(language).append('_').append(country).append('_').append(variant);  
            String key = sb.toString();  
            Locale locale = cache.get(key);  
            if (locale == null) {  
              locale = new Locale(language, country, variant);  
              Locale l = cache.putIfAbsent(key, locale);  
              
              if (l != null) {  
                locale = l;  
              }  
            }  
            return locale;  
          }  
          // ....  
      } 
    
    
    
    

remove

  • 方法说明

    boolean remove(Object key, Object value)

    如果key对应的值是value,则移除K-V,返回true。否则不移除,返回false

  • 使用场景

    删除Map中key和value

replace

  • 方法说明

    boolean replace(K key, V oldValue, V newValue)

    如果key对应的当前值是oldValue,则替换为newValue,返回true。否则不替换,返回false

  • 适用场景

    更新Map中的key对应的value

参考链接

  • 深入理解ConcurrentMap.putIfAbsent(key,value) 用法

    https://blog.youkuaiyun.com/exceptional_derek/article/details/40384659

  • ConcurrentHashMap中的remove方法的bug

    https://blog.youkuaiyun.com/killalllsd/article/details/83607945

  • Java ConcurrentHashMap多线程下的正确使用

    https://blog.youkuaiyun.com/guandongsheng110/article/details/80844574

  • 深入理解Java枚举

    https://www.cnblogs.com/ziph/p/13068923.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Charles Yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值