前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >多线程----ConcurrentHashMap

多线程----ConcurrentHashMap

作者头像
用户9854323
发布2022-06-25 10:55:44
1790
发布2022-06-25 10:55:44
举报
文章被收录于专栏:小陈飞砖

学习ConcurrentHashMap需要达到以下三点:

一、比较HashMap为什么不是线程安全的,及HashTable是如何实现的安全的,并且HashTable有什么问题?

二、深入理解CHM各项并发优化的原理。

三、掌握锁优化的方法。

一、比较HashMap为什么不是线程安全的,及HashTable是如何实现的安全的,并且HashTable有什么问题?

1、HashTable的问题(很暴力):

  • 大锁:直接对HashTable对象加锁。
  • 长锁:直接对方法加锁。
  • 读写锁共用:只有一把锁,从头锁到尾。

CHM对于HashTable的问题进行的优化:

  • 小锁:分段锁(5~7),桶节点锁(8)
  • 短锁:先尝试获取,失败再加锁
  • 分离读写锁:读失败再加锁(5~7),volatile读CAS写(7~8)
二、深入理解CHM各项并发优化的原理。
1、CHM的并发优化历程:
JDK1.5:分段锁,必要时加锁。

如下图对Key进行hash后,高位用来找segment,低位用来找table。

JDK1.6: 优化二次Hash算法。

因为JDK1.5时的hash算法会导致hashcode的高位不均匀分布,对于30000以下的整数key, hash出来后大部分集中在第16个segment中,对于50万以下的整数key,hash出来后大部分集中在第14,15个segment中,这样就退化为了HashTable。而JDK1.6时的hash算法实现高低位的均匀分布。

JDK1.7: 段懒加载(段不一开始就实例化,而是需要时实例化),使用volatile & cas 来避免加锁。

JDK1.7之前的16个segment在一开始全都实例化出来,但是JDK1.7之后需要哪个new哪个。因为segment是需要的时候创建,所以有可能多个线程访问的时候有可见性问题,所以JDK1.7大量的使用了volatile来保证segment的线程安全。

JDK1.8: 摒弃段,基于HashMap原理的并发实现。不必加锁的地方尽量使用volatile,必须加锁的地方如写入,尽量小范围的加锁。

jdk8开始,没有了段,所以只在hash的entry上加锁,缩小了范围。

为何放弃分段锁? 段Segment继承了重入锁ReentrantLock,有了锁的功能,每个锁控制的是一段,当每个Segment越来越大时,锁的粒度就变得有些大了。

2、各版本计数改进:

jdk5~7基于段元素个数求和,二次不同就加锁再求一次。 jkd8没有了段,引入CounterCell, 本质上也是分段计数。

3、CHM是弱一致性的:
  1. 添加元素后不一定能马上读到
  2. 清空后仍可能会有元素
  3. 遍历之前的段元素的变化会读到
  4. 遍历之后的段元素变化读不到
  5. 遍历时元素发生变化不抛异常
三、掌握锁优化的方法。

我们在多线程程序设计过程中可以借鉴什么呢?

  1. 长锁不如短锁,尽量只锁必要部分
  2. 大锁不如小锁,尽可能对加锁的对象进行拆分
  3. 公锁不如私锁,尽可能将锁的逻辑放在私有代码里
  4. 嵌套锁不如扁平锁,尽可能在代码设计时避免嵌套锁
  5. 分离读写锁,尽可能将读锁和写锁分离(如果大部分时间在读,只有少部分时间在写,那么给写加一个大锁,读加volatile或者不加锁)
  6. 粗化高频锁,尽可能合并处理频繁的短锁
  7. 消除无用锁,尽可能不加锁,或用volatile代替(可以保证原子性或者可见性的话)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、比较HashMap为什么不是线程安全的,及HashTable是如何实现的安全的,并且HashTable有什么问题?
  • 二、深入理解CHM各项并发优化的原理。
  • 1、CHM的并发优化历程:
    • JDK1.5:分段锁,必要时加锁。
      • JDK1.6: 优化二次Hash算法。
        • JDK1.7: 段懒加载(段不一开始就实例化,而是需要时实例化),使用volatile & cas 来避免加锁。
          • JDK1.8: 摒弃段,基于HashMap原理的并发实现。不必加锁的地方尽量使用volatile,必须加锁的地方如写入,尽量小范围的加锁。
            • 2、各版本计数改进:
              • 3、CHM是弱一致性的:
              • 三、掌握锁优化的方法。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档