ConcurrentHashMap是JDK1.5提供的线程安全的HashMap,它允许多个线程并发访问哈希表,并发修改map中的数据而不会产生死锁。ConcurrentHashMap适用于高并发的环境下,可以替代synchronized实现的同步HashMap。ConcurrentHashMap的并发度很高,吞吐量也很高。
ConcurrentHashMap底层采用“分段锁”机制,将数据分成一段段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问,
能够实现真正的并发访问。ConcurrentHashMap结构如下:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// 添加元素
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);
// 获取元素
Integer a = map.get("a");
Integer b = map.get("b");
Integer c = map.get("c");
// 大小和判断是否为空
int size = map.size();
boolean empty = map.isEmpty();
ConcurrentHashMap通过锁分段技术,实现高度的并发访问,大大提高了HashMap的吞吐量,是高并发环境下一个很好的选择。理解ConcurrentHashMap的原理和结构,可以更好的发挥其高性能特点。
ConcurrentHashMap在很多开源框架中广泛应用,这里举两个例子:
这两个例子都采用ConcurrentHashMap来存放数据,体现了它的 thread-safe 特性,可以在高并发场景下安全地操作数据。
在开发中,我们也要注意ConcurrentHashMap的一些操作技巧:
示例代码:
// 初始化大小
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>(32);
// entrySet遍历
for (Map.Entry<String, String> entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// do something
}
// mappingCount()计算精确大小
int size = map.mappingCount();
在实际项目中,ConcurrentHashMap的使用也需要考虑一些运维方面的内容:
这些运维方面内容,可以让ConcurrentHashMap在生产环境中运行更加稳定可靠。总之,ConcurrentHashMap是JDK提供的高性能Map实现,但在实际生产环境中,依然需要运维团队投入大量时间去监控、诊断和优化才能发挥其最高性能。
在JDK8中,ConcurrentHashMap进行了较大改进,比较重要的有两点:
改进代码示例:
static final int MIN_TREEIFY_CAPACITY = 64; //链表转换红黑树阈值
/**
* Initializes or doubles table size. If null, allocates in
* accord with initial capacity target held in field. Otherwise,
* because we are using power-of-two expansion, the elements from
* each bin must either stay at same index, or move with a power
* of two offset in the new table.
*
* @return the table
*/
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
// 超过最大值就不再扩充了,就只好随你碰撞去吧
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
// 没超过最大值,就扩充为原来的2倍
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= MIN_TREEIFY_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// 计算新的resize上限
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
......
}
可以看到JDK8在ConcurrentHashMap扩容和查询等机制上进行了比较大的改进,效率更高,是生产环境中更好的选择。
ConcurrentHashMap是Java中高性能的线程安全Map实现,通过锁分段技术实现高度并发。用它来替代同步的HashMap可以大大提高性能。
本文主要内容如下:
ConcurrentHashMap是一个复杂而高性能的组件,要充分理解其原理和机制,并在生产环境中结合运维知识进行监控和优化,才能发挥其最大效能。
希望本文能帮助大家充分理解ConcurrentHashMap的设计思想和实现机制。ConcurrentHashMap的高性能特性配合良好的运维手段,可以使系统整体吞吐量大幅增加,是高并发环境下一个很好的选择。