ConcurrentHashMap源码剖析

1. ConcurrentHashMap源码分析(JDK1.7) 1.1 Unsafe介绍 1.1.1 Unsafe简介

Unsafe类相当于是一个java语言中的后门类,提供了硬件级别的原子操作,所以在一些并发编程中被大量使用。jdk已经作出说明,该类对程序员而言不是一个安全操作,在后续的jdk升级过程中,可能会禁用该类。所以这个类的使用是一把双刃剑,实际项目中谨慎使用,以免造成jdk升级不兼容问题

1.1.2 Unsafe Api

这里并不系统讲解Unsafe的所有功能,只介绍和接下来内容相关的操作

arrayBaseOffset:获取数组的基础偏移量

arrayIndexScale:获取数组中元素的偏移间隔,要获取对应所以的元素,将索引号和该值相乘,获得数组中指定角标元素的偏移量

getObjectVolatile:获取对象上的属性值或者数组中的元素

getObject:获取对象上的属性值或者数组中的元素,已过时

putOrderedObject:设置对象的属性值或者数组中某个角标的元素,更高效

putObjectVolatile:设置对象的属性值或者数组中某个角标的元素

putObject:设置对象的属性值或者数组中某个角标的元素,已过时

1.1.3 代码演示 public class Test02 { public static void main(String[] args) throws Exception { Integer[] arr = {2,5,1,8,10}; //获取Unsafe对象 Unsafe unsafe = getUnsafe(); //获取Integer[]的基础偏移量 int baseOffset = unsafe.arrayBaseOffset(Integer[].class); //获取Integer[]中元素的偏移间隔 int indexScale = unsafe.arrayIndexScale(Integer[].class); //获取数组中索引为2的元素对象 Object o = unsafe.getObjectVolatile(arr, (2 * indexScale) + baseOffset); System.out.println(o); //1 //设置数组中索引为2的元素值为100 unsafe.putOrderedObject(arr,(2 * indexScale) + baseOffset,100); System.out.println(Arrays.toString(arr));//[2, 5, 100, 8, 10] } //反射获取Unsafe对象 public static Unsafe getUnsafe() throws Exception { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(null); } }

示意图

ConcurrentHashMap源码剖析

1.2 jdk1.7容器初始化 1.2.1 源码解析

无参构造

//空参构造 public ConcurrentHashMap() { //调用本类的带参构造 //DEFAULT_INITIAL_CAPACITY = 16 //DEFAULT_LOAD_FACTOR = 0.75f //int DEFAULT_CONCURRENCY_LEVEL = 16 this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); }

三个参数的构造:一些非核心逻辑的代码已经省略

//initialCapacity 定义ConcurrentHashMap存放元素的容量 //concurrencyLevel 定义ConcurrentHashMap中Segment[]的大小 public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { int sshift = 0; int ssize = 1; //计算Segment[]的大小,保证是2的幂次方数 while (ssize < concurrencyLevel) { ++sshift; ssize <<= 1; } //这两个值用于后面计算Segment[]的角标 this.segmentShift = 32 - sshift; this.segmentMask = ssize - 1; //计算每个Segment中存储元素的个数 int c = initialCapacity / ssize; if (c * ssize < initialCapacity) ++c; //最小Segment中存储元素的个数为2 int cap = MIN_SEGMENT_TABLE_CAPACITY; ////矫正每个Segment中存储元素的个数,保证是2的幂次方,最小为2 while (cap < c) cap <<= 1; //创建一个Segment对象,作为其他Segment对象的模板 Segment<K,V> s0 = new Segment<K,V>(loadFactor, (int)(cap * loadFactor), (HashEntry<K,V>[])new HashEntry[cap]); Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize]; //利用Unsafe类,将创建的Segment对象存入0角标位置 UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0] this.segments = ss; }

综上:ConcurrentHashMap中保存了一个默认长度为16的Segment[],每个Segment元素中保存了一个默认长度为2的HashEntry[],添加的元素是存入对应的Segment中的HashEntry[]中。所以ConcurrentHashMap中默认元素的长度是32个,而不是16个

示意图:

ConcurrentHashMap源码剖析

1.2.2 Segment是什么? static final class Segment<K,V> extends ReentrantLock implements Serializable { ... }

Segment是继承自ReentrantLock的,它可以实现同步操作,从而保证多线程下的安全。因为每个Segment之间的锁互不影响,所以也将ConcurrentHashMap中的这种锁机制称之为分段锁,这比HashTable的线程安全操作高效的多

1.2.3 HashEntry是什么? //ConcurrentHashMap中真正存储数据的对象 static final class HashEntry<K,V> { final int hash; //通过运算,得到的键的hash值 final K key; // 存入的键 volatile V value; //存入的值 volatile HashEntry<K,V> next; //记录下一个元素,形成单向链表 HashEntry(int hash, K key, V value, HashEntry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } } 1.3 jdk1.7添加操作 1.3.1 源码分析

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpsdsy.html