并发环境下为什么使用ConcurrentHashMap
-
HashMap在高并发的环境下,执行put操作会导致HashMap的Entry链表形成环形数据结构,从而导致Entry的next节点始终不为空,因此产生死循环获取Entry。
-
HashTable虽然是线程安全的,但是效率低下,当一个线程访问HashTable的同步方法时,其他线程再访问它记得等锁释放,锁的粒度太大了。
java 1.7 ConcurrentHashMap 数据结构图
ConcurrentMashmap的一些主要字段:
static final int DEFAULT_INITIAL_CAPACITY = 16; //默认sement[]的容量
static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认加载因子
static final int DEFAULT_CONCURRENCY_LEVEL = 16; //默认并发级别 ,即segmen[]的长度
static final int MAXIMUM_CAPACITY = 1 << 30; //table[]的最大容量,2^30
static final int MIN_SEGMENT_TABLE_CAPACITY = 2; //segment对象中table[]的最小容量
static final int MAX_SEGMENTS = 1 << 16; //segment[]的最大长度,2^16
final Segment<K,V>[] segments; //主要数据域
Segment的主要字段:
static final class Segment<K,V> extends ReentrantLock implements Serializable {
//为一个操作Segment的线程获取锁的最大尝试次数(若到达这个次数后,该线程会陷入阻塞)
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
transient int threshold; //扩容阈值
final float loadFactor; //加载因子
transient volatile HashEntry<K,V>[] table; //散列表
......
}
java 1.8 ConcurrentHashMap 数据结构图
- 改进一:取消segments字段,直接锁Node,也就是每次只锁一条链,进一步减少并发冲突的概率
- 改进二:单向链表变长后转为红黑树的结构。
- 改进三:用CAS自旋锁和sychronized 代替 ReentrantLock。查找,替换,赋值操作都是用CAS效率比ReentrantLock要高,1.8之后sychonize 性能升级,在扩容时才用到sychronized