• HashTable, HashMap, ConcurrentHashMap 之间的区别


            HashMap: 线程不安全. key 允许为 null。

            Hashtable: 线程安全. 使用 synchronized Hashtable 对象, 效率较低. key 不允许为 null.。只是简单的把关键方法上加上了 synchronized 关键字。如 get 和 set ,这相当于直接针对 Hashtable 对象本身加锁,如果多线程访问同一个 Hashtable 就会直接造成锁冲突.。size 属性也是通过 synchronized 来控制同步, 也是比较慢的。一旦触发扩容, 就由该线程完成整个扩容过程. 这个过程会涉及到大量的元素拷贝, 效率会非常低。当多个线程进行一些更复杂的操作,比如判定get 的值就是xxx,再进行put 就会产生线程安全问题。例如 

    Hashtable hash = new Hashtable<>();
    if(hash.get(1) == null){
        hash.put(1, 2);
    }

    虽然get是加锁的,但这里的 if 和 put 不是原子的,当多个线程线程同时进行这个操作的话,就会导致前一个线程判断为空,另一个线程也判断为空,这就导致这个操作进行了多次,但实际上只需要进行一次,这就出现了线程安全问题

            ConcurrentHashMap: 线程安全. 使用 synchronized 锁每个链表头结点, 锁冲突概率低, 充分利用CAS 机制. 优化了扩容方式. key 不允许为 null。相比于 Hashtable 做出了一系列的改进和优化. Java1.8 为例:

    1)读操作没有加锁(但是使用了 volatile 保证从内存读取结果), 只对写操作进行加锁.

    2)加锁的方式仍然是是用 synchronized, 但是不是锁整个对象, 而是 "锁桶" (用每个链表的头结点作为锁对象), 减小了锁的粒度,大大降低了锁冲突的概率.。

    3)充分利用 CAS 特性. 比如 size 属性通过 CAS 来更新. 避免出现重量级锁的情况.。

    4)优化了扩容方式: 化整为零 ,发现需要扩容的线程, 只需要创建一个新的数组, 同时只搬几个元素过去,扩容期间, 新老数组同时存在。后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程, 每个操作负责搬运一小部分元素,搬完最后一个元素再把老数组删掉,这个期间, 插入只往新数组加, 查找需要同时查新数组和老数组。

    ConcurrentHashMap每个哈希桶都有一把锁,只有两个线程同时访问同一个哈希桶才会发生锁冲突。

  • 相关阅读:
    xorm数据库操作之Join、Union
    【UI自动化】微信群聊未添加人数统计
    Python爬虫抓取微博数据及热度预测
    热门Java开发工具IDEA入门指南——创建新的Java应用程序(中)
    初学vue.js
    fastdfs--nginx安全漏洞(CVE-2021-23017)修复
    金仓数据库KingbaseES数据库开发指南(5. 面向应用程序的高级功能)
    LAMP和LNMP架构详解
    Mysql之增删改查
    动态内存管理(含柔性数组的讲解和经典笔试题的讲解)
  • 原文地址:https://blog.csdn.net/LTY3301398968/article/details/133521474