JDK源码分析(二)—— ConcurrentHashMap
一、概述
ConcurrentHashMap是Java5中新增加的一个线程安全的Map集合,可以用来替代HashTable。
锁分段技术
原理:将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
理解Java存储模型(JMM)的Happens-Before规则
一个例子
public class Test1 {
private int a=1, b=2;
public void foo(){ // 线程1
a=3;
b=4;
}
public int getA(){ // 线程2
return a;
}
public int getB(){ // 线程2
return b;
}
}
当线程1执行foo方法的时候,线程2访问getA和getB会得到什么样的结果?
A:a=1, b=2 // 都未改变
B:a=3, b=4 // 都改变了
C:a=3, b=2 // a改变了,b未改变
D:a=1, b=4 // b改变了,a未改变
多线程之间内存可见性(Visibility)顺序不一致的问题。有两种可能会造成上面的D选项。
- Java编译器的重排序
- 假设代码有两条语句,代码顺序是语句1先于语句2执行;那么只要语句2不依赖于语句1的结果,打乱它们的顺序对最终的结果没有影响的话,那么真正交给CPU去执行时,他们的顺序可以是没有限制的。可以允许语句2先于语句1被CPU执行,和代码中的顺序不一致。
- 从线程工作内存写回主存时顺序无法保证
JMM中一个重要问题就是:如何让多线程之间,对象的状态对于各线程的“可视性”是顺序一致的。它的解决方式就是 Happens-before 规则:
JMM为所有程序内部动作定义了一个偏序关系,叫做happens-before。要想保证执行动作B的线程看到动作A的结果(无论A和B是否发生在同一个线程中),A和B之间就必须满足happens-before关系。
后续分析ConcurrenHashMap时也会看到使用到锁(ReentrantLock),Volatile,final等手段来保证happens-before规则的。
使用锁方式实现“Happens-before”是最简单,容易理解的。
二、源码
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable {
static class Segment<K,V> extends ReentrantLock implements Serializable {
private static final long serialVersionUID = 2249069246763182397L;
final float loadFactor;
Segment(float lf) { this.loadFactor = lf; }
}
}
关于作者
后端程序员,五年开发经验,从事互联网金融方向。技术公众号「清泉白石」。如果您在阅读文章时有什么疑问或者发现文章的错误,欢迎在公众号里给我留言。