Java HashMap笔记之三:ConcurrentHashMap线程安全原理

xiaoxiao2021-02-28  8

在《Java HashMap笔记之一:基本原理》和《Java HashMap笔记之二:线程不安全原理》已经介绍了Java中HashMap的基本工作原理和线程不安全的原理这里先简单回顾总结下:

内部Entry数组默认大小是16,默认负载因子是0.75

内部Entry数组大小是2的幂

元素个数超过当前大小*默认因子的时候会扩容到当前大小的2倍

扩容是为了减少单个Entry数组链表的平均长度

HashMap线程不安全的主要因素的put过程中会发生扩容,多个线程会同时操作同一块内存导致

JDK7使用数组+链表方式实现;JDK8使用数组+链表/红黑树的方式实现:链表长度为8,链表转化为红黑树;红黑树节点个数为6,红黑树会转化为链表

HashMap是线程不安全的,在多线程环境下,可以使用HashTable和ConcurrentHashMap。HashTable的get和put方法都使用synchronized修饰,导致线程1在get元素的时候,线程2不仅不可以put元素,也不可以get元素,在竞争环境激烈的情况下,会出现比较严重的性能问题。下面我们来看下ConcurrentHashMap使用哪种方式解决了性能问题(以JDK7为示例)。

ConcurrentHashMap基本思想

ConcurrentHashMap数据结构

ConcurrentHashMap的初始化

ConcurrentHashMap的get方法

ConcurrentHashMap的put方法

ConcurrentHashMap的size方法

ConcurrentHashMap的弱一致性

举2个比较简单的例子就可以看出ConcurrentHashMap的弱一致性。

第一个,就是clear方法。假设线程1执行到Segment[1].clear()的时候,Segment[0]中的元素元素已经被clear,但是线程2此时可以在Segment[0]中增加元素(clear不需要加锁),这样线程1执行结束的时候就ConcurrentHashMap并不是空的。

第二个例子,判断key是否存在,不存在则进行put。假设线程1判断key不存在后挂起,线程2判断key也不存在,然后线程2执行put操作,之后线程1执行put操作,此时线程2读取到的会是线程1设置的值而不是线程2设置的值。当然此时应该是应用putIfAbsent方法。

小结

ConcurrentHashMap在设计思路对效率和一致性上的平衡以及一些lock-free的方法非常值得借鉴,同时注意虽然类本身是线程安全的,但是不要认为使用该类就一定是线程安全的。

转载请注明原文地址: https://www.6miu.com/read-1250104.html

最新回复(0)