Unsafe
ConcurrentHashMap使用Synchronized和CAS锁对线程进行并发控制。CAS锁,也就是自旋锁,即比较并交换。ConcurrentHashMap的CAS锁是使用jdk.internal.misc.Unsafe类实现的,而jdk.internal.misc.Unsafe实际是调用sun.misc.Unsafe。
sun.misc.Unsafe的无参构造函数是私有private的,所以不能直接通过new实例化的方式获得一个对象,只能通过反射的方式获取。sun.misc.Unsafe类提供了三个CAS方法:compareAndSwapInt、compareAndSwapLong和compareAndSwapObject,分别是对int、long和object类型的变量做比较交换。这三个方法都有4个入参,第一个参数表示要作用的对象,第二个参数表示要作用的变量,第三个参数表示期望值,第四个参数是修改变成的值,当变量的值与第三个参数相同时,才会将变量的值修改为第四个参数,如果修改成功会返回true,修改失败返回false。示例如下:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Person {
public int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@Test
public void testUnsafe() {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Person person = new Person();
try {
Unsafe unsafe = (Unsafe) unsafeField.get(null);
//定义要作用在哪个变量上
final long COUNT_OFFSET = unsafe.objectFieldOffset(person.getClass().getDeclaredField("age"));
boolean b = unsafe.compareAndSwapInt(person, COUNT_OFFSET, 0, 1);
System.out.println(b);
System.out.println(person.getAge());
} catch (Exception e) {
e
.printStackTrace();
}
}
comparableClassFor(Object x)
假设对象x的Class类型为C,如果Class C实现了Comparable接口,Comparable有且仅有一个泛型参数,是这个类本身C,也就是:
class C implements Comparable<C>
调用ConcurrentHashMap的comparableClassFor()方法会返回C.class。
static Class<?> comparableClassFor(Object x) {
//判断是否是Comparable的子类,如果不是直接返回null
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; ParameterizedType p;
//如果参数x的Class类型是String,直接返回class java.lang.String
if ((c = x.getClass()) == String.class) // bypass checks
return c;
//获取实现的父类接口类型
if ((ts = c.getGenericInterfaces()) != null) {
//遍历接口类型
for (Type t : ts) {
//判断该接口是否实现了泛型 && 接口的类型对象是否是Comparable
// 泛型参数只有一个 && 泛型参数类型就是入参的Class类型
if ((t instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() == Comparable.class) && (as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c)
return c;
}
}
}
return null;
}
Class的getGenericInterfaces()方法返回的是实现的接口的类型数组。
public Type[] getGenericInterfaces() {
ClassRepository info = getGenericInfo();
return (info == null) ? getInterfaces() : info.getSuperInterfaces();
}
接口ParameterizedType继承了接口Type。ParameterizedType是带有类型参数的类型,即常说的泛型,如Collection。
Type[] getActualTypeArguments(); //返回一个Type数组,数组里是参数化类型的参数
Type getRawType();//返回声明此类型的类或接口
示例:
List<String> list = new ArrayList<String>();
Type[] genericInterfaces = list.getClass().getGenericInterfaces();
for(Type type:genericInterfaces) {
if(type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
System.out.println(Arrays.asList(parameterizedType.getActualTypeArguments()));//[E]
System.out.println(parameterizedType.getRawType());//interface java.util.List
}
}
compareComparables(Class<?> kc, Object k, Object x)
compareComparables是用来比较两个对象的大小。如果x为空,或者类型不是kc,返回0。如果x不为空并且x的类型是kc,返回k.compareTo(x)的比较结果。
static int compareComparables(Class<?> kc, Object k, Object x) {
return (x == null || x.getClass() != kc ? 0 :
((Comparable)k).compareTo(x));
}
ConcurrentHashMap插入元素
ConcurrentHashMap.put()方法会调用putVal()方法插入元素。putVal()方法的第三个布尔变量onlyIfAbsent如果为true,表示只有在不存在相同的元素(hashcode和key值相等)才允许插入。如果存在相同的元素,putVal()方法会返回旧值,否则,返回null。与HashMap不同的是,ConcurrentHashMap不支持null键和null值。
(1)如果数组为null或者长度为0,初始化数组。
(2)根据hash算出待插入元素存放在数组的下标,判断该下标是否存在元素,如果不存在,使用CAS锁直接插入元素。
(3)判断数组是否正在扩容,如果是,当前线程加入扩容。
(4)判断该下标位置上的元素与待插入元素的hash和key是否相等,如果相等,表示是同一个元素,直接返回。
(5)对该下标位置的元素进行Synchronized同步,遍历链表/红黑树,插入元素。链表使用尾插法。
(6)计数。
final V putVal(K key, V value, boolean onlyIfAbsent) {
//如果key或者value为null,抛出空指针异常。
if (key == null || value == null) throw new NullPointerException();
//将高16位和低16位进行异或运算,目标是得到尽可能不同的值。
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
//i表示待插入元素放在数组位置的下标,f表示tab[i]的元素,n表示tab的长度
//fh表示tab[i]元素的hash值,fk表示tab[i]元素的key,fv表示tab[i]元素的value值
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
//当数组为空或者数组的长度为0,对数组进行初始化
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//如果f为空,表示tab[i]没有存放元素,使用cas锁,期望值是null,最后替换为待插入节点(hash, key, value)
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; //跳出循环,向空槽中添加元素不会使用锁
}
else if ((fh = f.hash) == MOVED)
//如果tab[i]的hash值是MOVED(-1),表示正在进行扩容,当前线程也会加入扩容
tab = helpTransfer(tab, f);
else if (onlyIfAbsent
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key

本文详细剖析了Java 8中ConcurrentHashMap的实现原理,包括其并发控制机制、CAS锁与Unsafe类的应用、扩容及树化过程、线程安全的计数方法。通过对内部数据结构和操作细节的讲解,展示了ConcurrentHashMap如何在高并发场景下保证性能和线程安全。
最低0.47元/天 解锁文章
935

被折叠的 条评论
为什么被折叠?



