• Java HashMap 实现线程安全


    非线程安全。(非同步)

    怎么才能让 HashMap 变成线程安全的呢?

    实现方法

    我认为主要可以通过以下三种方法来实现:

    1.替换成Hashtable

    Hashtable通过对整个表上锁实现线程安全,因此效率比较低
    [[Java HashTable]]

    2.使用Collections类的 synchronizedMap 方法包装一下

    (不是绝对的线程安全)
    [[Java synchronizedMap]]

    3.使用ConcurrentHashMap

    它使用分段锁来保证线程安全
    通过前两种方式获得的线程安全的HashMap在读写数据的时候会对整个容器上锁,而ConcurrentHashMap并不需要对整个容器上锁,它只需要锁住要修改的部分就行了

    [[Java ConcurrentHashMap]]

    Java HashTable

    HashTable 中所有 CRUD 操作

    都是线程同步的,同样的,线程同步的代价就是效率变低了。
    所有的读写操作都进行了锁保护,是线程安全的。

    Hashtable 中的常用变量

    private transient Entry<?,?>[] table; //底层保存节点的数组
    private transient int count;  //记录表中的节点数
    private int threshold;   //阈值,当表的节点数>=该阈值,会进行扩容
    private float loadFactor;  //负载因子,计算阈值用的
    private transient int modCount = 0;  //Hashtable在结构上被修改的次数
    // 计算节点在数组的下标
    int index = (hash & 0x7FFFFFFF) % tab.length; 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    HashTable 中的常用方法

    构造方法

    contains()

    public synchronized boolean containsKey(Object key) {
    	Entry<?,?> tab[] = table; //表
    	int hash = key.hashCode();
    	//计算key在数组中的下标
    	int index = (hash & 0x7FFFFFFF) % tab.length; 
    	//遍历链表
    	for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
    		if ((e.hash == hash) && e.key.equals(key)) {
    			return true;
    		}
    	}
    	return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Java synchronizedMap

    方法如下:

    // 返回由指定映射支持的同步(线程安全的)映射
    public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)  
    
    private static Map<String,List<MaxMinTimeValue>> maxMinTimeValueMap = Collections.synchronizedMap(new LinkedHashMap<String,List<MaxMinTimeValue>>());
    private static Map<String,MaxMinTimeValue> maxMinTimeValueMap4Kuashiduan = Collections.synchronizedMap(new LinkedHashMap<String,MaxMinTimeValue>());
    
    • 1
    • 2
    • 3
    • 4
    • 5

    它是将map的每个方法都加了同步(都加了虚拟锁机制)

    但是他们放在一起操作(多个该map(map是通过Collections.synchronizedMap来定义的)的方法)还是会出现线程不安全的事情。如当程序正在执行containsKey方法时,另一个程序已经将key删除了。会出现这种情况。就是会有两个线程同时操作map,导致不可预见性的结果。这时为了解决此问题,还是要加上同步机制。如:

    private static final Object object=new Object();
    
    public void removeKey(String key){
    	synchronized(object){
    		if(map.containsKey(key)){//containsKey方法是线程安全的方法
    			map.remove(key);//remove方法也是线程安全的方法。
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    故 Collections.synchronizedMap 也只是有条件的线程安全的。而不是绝对的线程安全的。还是少用的好。

    Java ConcurrentHashMap

    简介

    再 Java 1.5 版本引入 ConcurrentHashMap,实现线程安全。

    ConcurrentHashMap 将 hash 表分为 16 个桶(默认值),诸如 get,put,remove 等常
    用操作只锁当前需要用到的桶。试想,原来只能一个线程进入,现在却能同时 16 个写线程进入(写线程
    才需要锁定,而读线程几乎不受限制,并发性的提升是显而易见。

    ConcurrentHashMap是一个哈希表,支持检索的全并发和更新的高预期并发。此类遵循与 Hashtable 相同的功能规范,并包含 Hashtable 的所有方法。ConcurrentHashMap 位于 java.util.Concurrent 包中。

    • java.util.Concurrent.ConcurrentHashMap类通过将map划分为segment来实现线程安全,不是整个对象需要锁,而是一个segment,即一个线程需要一个segment的锁。
    • 在 ConcurrenHashap 中,读操作不需要任何锁。

    当一个线程迭代 Map 对象时,其他线程被允许修改 Map,我们不会得到 ConcurrentModificationException
    键和值都不允许为 Null。

    不同版本的 ConcurrentHashMap 的扩容

    JDK 1.7 版本

    略……

    JDK 1.8 版本

    • 1.8版本的 ConcurrentHashMap 不再基于 Segment 实现。
    • 当某个线程进行 put 时,如果发现 ConcurrentHashMap 正在进行扩容那么该线程一起进行扩容。
    • 当某个线程进行 put 时,如果没有正在进行扩容,则将 key-value 添加到 ConcurrentHashMap 中,然后判断是否超过阀值,超过了则进行扩容。
    • ConcurrentHashMap 是支持多个线程同时扩容的。
    • 扩容之前也先生成一个新的数组。
    • 在转移元素时,先将原数组分组,将每组分给不同的线程来进行元素的转移,每个线程负责一组或多组的元素转移工作。

    底层实现

    JDK 1.7 版本

    • 底层是数组 + 链表

    JDK 1.8 版本

    • 底层是数组 + 链表 + 红黑树,加红黑树的目的是提高 HashMap 插入和查询整体效率。
    • 链表插入使用的是尾插法,因为 1.8 中插入 key 和 value 时需要判断链接元素个数,所以需要遍历链表统计链表元素个数,所以正好就直接使用尾插法。
    • 1.8 中简化了 Hash 算法,因为复杂的哈希算法目的就是提高散列性,来提供 HashMap 的整体效率,而 1.8 中新增了红黑树,所以可以适当的简化哈希算法,节省 CPU 资源。
  • 相关阅读:
    JavaFX作业
    如何设计一个网络爬虫?
    C_9练习题
    基于微信小程序的manster云音乐小程序
    声明式事务还是编程式事务,如何选择?
    项目性能优化—性能优化的指标、目标
    JUC-LockSupport与线程中断
    工具类Utils
    【数据结构】&&【C++】哈希表的模拟实现(哈希桶)
    java计算机毕业设计校园二手交易平台的设计源程序+mysql+系统+lw文档+远程调试
  • 原文地址:https://blog.csdn.net/wide288/article/details/127862145