落叶知秋|ConcurrentHashMap确实很复杂,这样学源码才简单( 二 )
如何解决呢?是不是可以简单粗暴的对这个put()方法加锁?当一个线程执行put()方法的时候 , 让其他线程只能等待 。
HashTable就是这样做的 , 可以打开HashTable的源码看到其put()方法如下:
Collections.synchronizedMap()中是定义了一个内部类SynchronizedMap:
包含传入的Map以及定义的Object类型的mutex对象 。
所有的方法都必须获得mutex对象的锁才能执行下去:
换个思路理解就是:在Map对象的外面装了一把锁 , 想访问Map对象必须获得mutex这把锁才可以 。
所以我们可以知道的是:
无论是HashTable还是Collections.synchronizedMap()都是通过synchronized关键字来保证线程安全的 。
即使现在的synchronized关键字在锁的优化上有了很大的性能提升 , 但是依旧不可避免的情况是:
在多线程竞争激烈的情况下 , 锁会升级至重量级锁 , 重量级锁是由操作系统所持有和分配的 , 也就意味着出现了用户态(我们的JVM进程)和内核态(操作系统的kernel进程)的切换 , 性能也就随之降低!
那么有没有那么一种方法避免这样的情况出现呢?当然有!不然咱们还聊什么?
这就是CAS , Compare And Swap!
也就是本文的主角ConcurrentHashMap的实现方式 , 它是采用了CAS无锁化的方式来保证了数组初始化的线程安全:
最核心的就是上面的initTable()方法了 , 最核心的就是这句话:
最核心的就是上面的**initTable()**方法了 , 最核心的就是这句话:同时要注意的是这里的几个成员变量都是volatile修饰的:transient volatile Node[] table;private transient volatile int sizeCtl; 4、其他线程不安全的操作上面主要是通过数组初始化这一步来讲HashMap是如何线程不安全的 , 并且ConcurrentHashMap中是如何保证线程安全的 。HashMap中线程不安全的地方 , ConcurrentHashMap中找对应功能的地方 , 看是怎么保证线程安全的就可以了 。
接下来我们继续看HashMap源码中那些线程不安全的操作 , 继续看putVal()方法:
大家如果细看上面的源码以及我写的注释的话 , 会发现初始化数组后 , 就开始要把对象put到刚刚初始化的数组中去了:
if ((p = tab[i = (n - 1) 那么上面这句话是线程安全的吗?当然不是 , 可以这么说 , 这种带if判断的都不是线程安全的 。
当一个线程判断当前这个tab[i]节点是null的时候 , 但是还没有执行完下一句 , 这个时候另外一个线程也来了 , 他也判断这个节点不是null , 也执行下面那句话 , 那最终两个线程只能有一个数据能写进去 , 另一个就丢了 。
推荐阅读
- 爆笑社|搞笑gif-哥们这样不好吧!不过看着确实有点远!
- 落叶知秋|跨境易税通应邀参加海关总署跨境电商研讨会
- 新鲜事儿|《浪姐》30岁蓝盈莹又被骂上热搜:这一次,确实是她不“懂事”了
- 落叶知秋|手机有这三种情况的,还是换了吧,别委屈自己
- 虎扑|已经想好踢巴萨的阵容,加图索:我确实应被罚下
- 杨易|《最强大脑》杨易夺冠实至名归,赵金昊实力全靠嘴?确实高看了
- 如今热刺俱乐部确实非常受欢迎,估计接下来穆里尼奥还会锁定新目标,然后完成签约
- 新机发布华为Mate40 Pro爆料:钢化膜曝光,确实很霸气!
- |清朝皇妃真容曝光:婉容长得确实漂亮,魏璎珞的原型堪称绝世美人
- 加图索|加图索:我确实应被罚下,已经想好踢巴萨的阵容
