ConcurrentHashMap源码jdk1.7及1.8的区别

线程安全的集合

  1. Hashtable
  2. ConcurrentHashMap
  3. Vector
    ConcurrentHashMap是HashMap的线程安全版本,ConcurrentSkipListMap是TreeMap的线程安全版本
    Hashtable是JDK 5之前Map唯一线程安全的内置实现(Collections.synchronizedMap不算)。Hashtable继承的是Dictionary(Hashtable是其唯一公开的子类),并不继承AbstractMap或者HashMap。尽管Hashtable和HashMap的结构非常类似,但是他们之间并没有多大联系。

为什么要使用ConcurrentHashMap

因为在并发编程中使用HashMap可能导致程序死循环,而HashTable效率低下。
1)HashMap是非线程安全的
在多线程的操作下、若调用HashMap.put()能引起死循环、导致CPU资源消耗大。
多线程操作put()会使得Entry链表形成环形数据结构,使得Entry的next节点永远不为空,就会产生死循环获取Entry。

2)HashTable的效率低
hashTable是用Synchronized来保证线程安全的,所有的线程都必须竞争一把锁、效率太低。
如线程A访问HashTable同步方法put()、B线程也访问同步方法put()、但B没有锁进入阻塞或轮询状态,也不能访问其他的方法,导致线程越多效率越低。

ConcurrentHashMap分析 jdk1.7

属性

int DEFAULT_INITIAL_CAPACITY = 16;  //初始容量的默认值-》hashentry的table属性
float DEFAULT_LOAD_FACTOR = 0.75f;  //默认的加载因子 -》HashEntry
int DEFAULT_CONCURRENCY_LEVEL = 16; //默认的并发度-》segment数组大小
int MAXIMUM_CAPACITY = 1 << 30;  //最大的容量 -》segment中数组的最大值 ->tab.length<MAXIMUM_CAPACITY
int MIN_SEGMENT_TABLE_CAPACITY = 2;  //HashEntry数组的最小值
int MAX_SEGMENTS = 1 << 16; // slightly conservative //最大并发度=segment数组的最大值
int RETRIES_BEFORE_LOCK = 2; //锁重试次数

final int segmentMask; //主要作用于keyhash过程
final int segmentShift;
final Segment<K,V>[] segments; //存储数据节点的位置

//存放数据的锁:
static final class Segment<K,V> extends ReentrantLock {
    //Segment是一种可重入锁(ReentrantLock),扮演锁的角色;
static final int MAX_SCAN_RETRIES =Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1; //segment获取锁重试次数上限
transient volatile HashEntry<K,V>[] table; //存放数据的
transient int count;
}
    
//HashEntry的属性
static final class HashEntry<K,V> {
   
       final int hash;
       final K key;
       volatile V value;
       volatile HashEntry<K,V> next;
  }

对HashEntry的value、next属性加上volatile,这样保证了可见性、
默认的ConcurrentHashMap中是有16个类似HashMap的结构、每个HashMap拥有一个独占锁。也就是说最终的效果就是通过某种Hash算法,将任何一个元素均匀的映射到某个HashMap的Map.Entry上面,而对某个一个元素的操作就集中在其分布的HashMap上,与其它HashMap无关。这样就支持最多16个并发的写操作。

构造函数

在这里插入图片描述
在这里插入图片描述

public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel) {
   }
                             //数组大小              加载因子           并发度,默认16个

segments数组长度ssize和concurrentLevel有关,ssize>=concurrentLevel的最小2次幂。
如果觉得这么描述不好理解,那么举个例子就清楚了。比如concurrentLevel=17,2的几次幂刚好>=17呢?是32对吧!所以ssize=32。更重要的是,Segment的数组大小之所以一定是2的次幂,就是为了方便通过按位与的散列算法来定位Segment的index位置

put()

通过加锁机制插入数据

1、定位数据应该放在哪个Segment中
通过hash算法定位到对应的Segment,若获取到的Segment为空,则调用ensureSegment()方法;
否则,直接调用查询到的Segment的put方法插入值.

ensureSegment()方法,使用getObjectVolatile()读取对应Segment,如果还是为空,则以segments[0]为原型创建一个Segment对象,并将这个对象设置为对应的Segment值并返回。

        if (value == null)   throw new NullPointerException();  
        int hash 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值