搞定HashMap线程不安全问题-----ConcurrentHashMap源码解析

本文深入解析HashMap线程不安全的原因,包括JDK1.7的环形链和数据丢失,以及JDK1.8的数据覆盖问题。并探讨保证线程安全的方法,如`synchronizedMap`、HashTable和ConcurrentHashMap。重点分析了ConcurrentHashMap在JDK1.7和1.8中的实现原理,包括其内部结构、锁粒度和volatile的使用,以及JDK1.8引入的红黑树优化。
摘要由CSDN通过智能技术生成

搞定HashMap线程不安全问题-----ConcurrentHashMap源码解析

前言

​ HashMap是线程不安全的集合,如果要保证线程安全该怎么做呢?

​ 首先,HashMap为什么会线程不安全?

  • ​ jdk1.7中,在多线程环境下,(头插法)扩容时会造成环形链或数据丢失。

  • ​ jdk1.8中,在多线程环境下,PUT方法会发生数据覆盖的情况。

​ 如何保证线程安全?

//替代HashMap的方式
public static void main(String[] args) {
   
        /**方式0**/
        HashMap<Object,Object> map = new HashMap<>();
        
        /**方式1**/
        Collections.synchronizedMap(new HashMap<>());

        /**方式2**/
        Hashtable hashtable = new Hashtable();

        /**方式3**/
        ConcurrentHashMap<Object,Object> concurrentHashMap = new ConcurrentHashMap<Object,Object>(32,0.75f,32);

}
  1. Collections.synchronizedMap(Map)创建线程安全的map集合

    ​ 看源码可知,synchronizedMap把创建的对象map赋给要同步的对象mutex,之后每次操作map都会在方法上加上锁。
    在这里插入图片描述

  2. HashTable

    ​ HashTable中对数据操作的大部分方法都会上锁,与方式1原理相似。如图中HashTable源码所示。

在这里插入图片描述

​ 缺点就是效率低,有些时候没必要加锁,却仍要线程等待。

​ hashtable的put方法:

在这里插入图片描述

  1. ConcurrentHashMap

    ​ HashMap是由Entry<K, V>[]数组(jdk1.7), Node<K, V>[]数组(jdk1.8)组成,

    ​ 而ConcurrentHashMap是由 Segment[HashEntry[]] (jdk1.7),Node<K, V>[]数组(jdk1.8)组成。

    ​ 在jdk1.7中ConcurrentHashMap的底层数据结构是数组(Segment)加链表(HashEntry),可以看到内部类Segment的结构

    /** Segment类继承于ReentrantLock类,所以Segment本质上是一个可重入的互斥锁 */
    static final class Segment<K,V> extends ReentrantLock implements Serializable {
         
    
        private static final long serialVersionUID = 2249069246763182397L;
    
        //存放数据的节点,与HashMap不同,用volatile修饰value和next节点
        transient volatile HashEntry<K,V>[] table;
    
        transient int count;
        transient int modCount;
        transient int threshold;
        final float loadFactor;
    
    }
    

    ​ 在来看jdk1.7中ConcurrentHashMap的put方法

    /** 1 先定位到Segment,然后进行put操作 */
    public V put(K key, V value) {
         
        Segment<K,V> s;
        //注意不能put空value
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key);
        int j = (hash >>> segmentShift) & segmentMask;
        // 如果找不到该Segment,则新建一个。
        if ((s = (Segment<K,V>)UNSAFE.getObject          
             (segments, (j << SSHIFT)<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值