当前位置: 首页 > news >正文

【Java复习】线程安全的 HashMap --- ConcurrentHashMap

目录

  • 1. HashTable 与HashMap区别
  • 2. 为什么不使用HashTable
  • 3. ConcurrentHashMap (jdk 1.7 版本)
  • 4. ConcurrentHashMap 1.7底层实现原理
  • 5. # 4. ConcurrentHashMap 1.8 底层实现原理

1. HashTable 与HashMap区别

  • HashTable 线程是安全的 (使用synchronized)
  • HashMap 线程是不安全 (没有使用锁)
  • HashMap 允许存放key值 null 存放在 index=0位置
  • HashTable 不允许存放key为null
  • HashMap实现不同步,线程不安全。 HashTable线程安全 HashMap中的key-value都是存储在Entry中的。
  • 继承不同。
    Hashtable 继承 Dictionary 类,而 HashMap 继承 AbstractMap
    public class Hashtable extends Dictionary implements Map
    public class HashMap extends AbstractMap implements Map
  • Hashtable中的方法是同步的,而HashMap中的方法在缺省情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
  • Hashtable 中, key 和 value 都不允许出现 null 值。 在 HashMap 中, null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null 。
  • 当get() 方法返回 null 值时,即可以表示 HashMap 中没有该键,也可以表示该键所对应的值为 null 。因此,在 HashMap 中不能由 get() 方法来判断
  • HashMap 中是否存在某个键, 而应该用 containsKey() 方法来判断。
  • 哈希值的使用不同,HashTable直接使用对象的hashCode。而HashMap重新计算hash值

2. 为什么不使用HashTable

HashTable 底层 通过 synchronized 保证线程安全性问题
保证线程安全性问题—加上锁—发生锁的竞争

HashTable 当多个线程 在访问 get或者put操作的时候会发生this锁的竞争,多个线程竞争锁 最终只会有一个线程获取到this锁,获取不到的this锁 可能会阻塞等待。最终将我们的 HashTable 中 get 或者put方法改成单线程执行 效率是非常的低。

在多线程的情况下 不推荐使用 HashTable
而是推荐使用高效率的 ConcurrentHashMap

使用传统HashTable保证线程问题,是采用synchronized锁将整个HashTable中的数组锁住,在多个线程中只允许一个线程访问Put或者Get,效率非常低,但是能够保证线程安全问题。

在这里插入图片描述
Jdk官方不推荐在多线程的情况下使用HashTable或者HashMap,建议使用ConcurrentHashMap分段HashMap,效率非常高。

ConcurrentHashMap 核心思想:减少多个线程锁竞争 不会在访问同一个HashTable

3. ConcurrentHashMap (jdk 1.7 版本)

就是将一个大的HashTable集合拆分成n多个小的HashTable集合 默认16个(使用分段锁设计)

在多线程的情况下访问到我们的ConcurrentHashMap 1.7版本做写的操作,如果多个线程写入的key 最终计算落地到不同小的HashTable集合中,就可以实现多线程同时写入key 不会发生锁的竞争。

如果多个线程写入的key 最终计算落地到同一个小的HashTable集合中,就会发生锁的竞争。

ConcurrentHashMap get方法没有锁的竞争
HashTable get方法有锁的竞争

4. ConcurrentHashMap 1.7底层实现原理

ConcurrentHashMap将一个大的HashTable集合拆分成n多个不同的小的HashTable(Segment),默认的情况下是分成16个不同的Segment。每个Segment中都有自己独立的HashEntry<K,V>[] table
在这里插入图片描述
手写 ConcurrentHashMap 1.7 :

import java.util.Hashtable;

public class MyConcurrentHashMap<K,V> {
    private Hashtable<K,V>[] hashtables;

    public MyConcurrentHashMap(){
        hashtables=new Hashtable[16];
        for (int i = 0; i < hashtables.length; i++) {
            hashtables[i]=new Hashtable<>();
        }
    }
    public void put(K k,V v){
        int hashTableIndex=k.hashCode()%hashtables.length;
        hashtables[hashTableIndex].put(k,v);
    }

    public V get(K k){
        int hashTableIndex=k.hashCode()%hashtables.length;
        return hashtables[hashTableIndex].get(k);
    }

    public static void main(String[] args) {
        MyConcurrentHashMap<String,String> myConcurrentHashMap=new MyConcurrentHashMap<>();
        myConcurrentHashMap.put("xinshang","mengmeng");
        System.out.println(myConcurrentHashMap.get("xinshang"));
    }

}

ConcurrentHashMap 底层采用 分段锁设计 将一个大的HashTable线程安全的集合拆分成n多个小的 HashTable集合,默认初始化16个小的HashTable集合。
如果同时16个线程 最终计算index值 落地到不同的小的HashTable集合 不会发生锁的竞争,同时可以支持16个线程 访问ConcurrentHashMap 写的操作,效率非常高

注意点:ConcurrentHashMap 1.7版本需要计算出2次 index值。

  • 第一次计算index= 计算key存放到具体小的HashTable
  • 第二次计算index= 计算key存放到具体小的HashTable,对应具体 数组 index位置。
    (HashTable----数组+链表实现)

5. # 4. ConcurrentHashMap 1.8 底层实现原理

在 JDK 1.7 中,ConcurrentHashMap 虽然是线程安全的,但因为它的底层实现是数组 + 链表的形式,所以在数据比较多的情况下访问是很慢的,因为要遍历整个链表,而 JDK 1.8 则使用了数组 + 链表/红黑树的方式优化了 ConcurrentHashMap 的实现,具体实现结构如下:
在这里插入图片描述
链表升级为红黑树的规则:当链表长度大于 8,并且数组的长度大于 64 时,链表就会升级为红黑树的结构。

ConcurrentHashMap 1.8版本 取消了分段锁设计,改成了直接锁表头(数组+链表+红黑树)
在 JDK 1.8 中,ConcurrentHashMap 是在头节点加锁来保证线程安全的,锁的粒度相比 Segment 来说更小了,发生冲突和加锁的频率降低了,并发操作的性能就提高了。而且 JDK 1.8 使用的是红黑树优化了之前的固定链表,那么当数据量比较大的时候,查询性能也得到了很大的提升,从之前的 O(n) 优化到了 O(logn) 的时间复杂度,具体加锁示意图如下:
在这里插入图片描述

  • over ~

相关文章:

  • 《文化相对论》:危机重重的世界,对话才能产生转机
  • 水溶性CuInS/ZnS 量子点 PL 550 nm--800 nm
  • Vue3和react状态管理之Redux与Pinia的使用比较
  • 新学期如何克服“社恐”,猿辅导老师给高中生三条建议
  • 浅析各种主流区块链共识算法的利与弊
  • [架构之路-16]:目标系统 - 硬件平台 - CPU主要物理性能指标
  • 【JAVASE】JDK8新特性
  • 【0121】建立与postgres后端的连接(2)
  • 交换机、IP地址、ARP协议
  • 迭代器并不全是指针,list的迭代器与vector和string的有什么不一样,让博主告诉你其底层原理!
  • 80页4万字政务综合服务平台建设项目方案书(完整版)
  • Python03--python中的标识符和变量以及数据类型
  • 微信小程序开发实战(API及多人协调开发)
  • 顺序表❀数据结构
  • SpringBoot开发之SpringJDBC
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • Angular 响应式表单 基础例子
  • django开发-定时任务的使用
  • es的写入过程
  • HTTP那些事
  • JavaScript对象详解
  • Javascript设计模式学习之Observer(观察者)模式
  • JS 面试题总结
  • js正则,这点儿就够用了
  • Lsb图片隐写
  • Redis在Web项目中的应用与实践
  • unity如何实现一个固定宽度的orthagraphic相机
  • vue--为什么data属性必须是一个函数
  • 关于 Linux 进程的 UID、EUID、GID 和 EGID
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 深入浅出Node.js
  • 使用 Node.js 的 nodemailer 模块发送邮件(支持 QQ、163 等、支持附件)
  • 用Node EJS写一个爬虫脚本每天定时给心爱的她发一封暖心邮件
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 白色的风信子
  • mysql面试题分组并合并列
  • ​520就是要宠粉,你的心头书我买单
  • ​你们这样子,耽误我的工作进度怎么办?
  • ​云纳万物 · 数皆有言|2021 七牛云战略发布会启幕,邀您赴约
  • # centos7下FFmpeg环境部署记录
  • (1)(1.11) SiK Radio v2(一)
  • (12)目标检测_SSD基于pytorch搭建代码
  • (AngularJS)Angular 控制器之间通信初探
  • (C语言)fread与fwrite详解
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (笔记)Kotlin——Android封装ViewBinding之二 优化
  • (附源码)计算机毕业设计SSM疫情社区管理系统
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (原創) 未来三学期想要修的课 (日記)
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)EOS中账户、钱包和密钥的关系
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)shell调试方法
  • (转)可以带来幸福的一本书