currenthashmap底层原理

currenthashmap底层原理


2024年3月10日发(作者:)

currenthashmap底层原理

ConcurrentHashMap是Java中的一个线程安全的哈希表,也是Java并发编程领域中非

常重要的一个类。它可以用来代替Hashtable和同步的HashMap,因为相对于这两个类,

它有更好的并发性能和更低的锁竞争。

ConcurrentHashMap是如何实现线程安全的呢?

ConcurrentHashMap是通过分割数组来实现线程安全的。它将一个数组分割成多个小

的数组片段(segment),每个片段都有一个独立的锁来控制对于该片段的访问。这样多个

线程可以同时访问不同的片段,从而避免了像Hashtable一样锁住整个数据结构来保证线

程安全的问题。

每个片段都是一个类似HashMap的结构,也即是一个桶数组(table),每个桶数组的

元素都是一个链表(链式结构)。每个键值对的插入或读取都是在相应的片段上进行的,

不同的片段之间是并发的。

ConcurrentHashMap在数据结构的基础上,还使用了一些工程上的优化,比如弱一致

性的迭代器、批量操作等,从而提高了效率,降低了锁竞争。

下面我们来具体探讨ConcurrentHashMap中的几个关键方法。

1. put方法分析

首先我们看一下put方法的源码:

```

public V put(K key, V value) {

Segment s;

if (value == null)

throw new NullPointerException();

int hash = hash(de());

int j = (hash >>> segmentShift) & segmentMask;

if ((s = (Segment)segments[j]) == null) {

s = ensureSegment(j);

}

return (key, hash, value, false);

}

```

其中的Segment代表ConcurrentHashMap中的每个小片段,segmentShift和

segmentMask是用于计算片段索引的常量,segments代表所有的片段数组。

这段代码的作用是向ConcurrentHashMap中添加一个键值对,其中put方法是通过调

用每个小片段的put方法来完成。先通过key来计算出哈希值hash,然后再通过hash计算

出该键值对所属的片段索引j,如果该索引的小片段不存在,则新建一个小片段,并将其

赋值给s。

最终是通过调用小片段的put方法将键值对添加到具体的桶中。在将键值对添加到桶

中之前,先通过compareAndSwapObject方法(基于CAS操作)判断该桶是否已经有元素。

如果有元素,则遍历链表来确定该键值对是否已经存在于链表中或者需要添加到链表末尾。

如果没有元素,则直接将该键值对添加到桶中即可。

与put方法类似,get方法中也是首先通过哈希值计算出该键值对所属的小片段,然

后在片段中根据该键值对的哈希值查找包含该键值对的桶。如果桶中有相应的键值对,则

返回该键值对的值,否则返回null。

ConcurrentHashMap中定义了一个专门用于遍历哈希表的迭代器

rator,它继承至抽象类erator,

并实现了接口Iterator。

具体来说,该迭代器的实现方式是弱一致性的(weakly consistent)。在遍历哈希表

时,在遍历完某个片段后,首先会尝试更新该片段中被删除的元素等,以确保后续的遍历

可以跳过这些已经删除的元素。由于实际上是弱一致性的,因此在遍历范围内可能还是存

在并发修改,但是会保证迭代器可以遍历到所有现存的元素,并且不会重复,也不会“挂

起”(ConcurrentModificationException)。

ConcurrentHashMap是通过分割数组来实现线程安全的,每个小片段都有一个独立的

锁来控制对于该片段的访问,对于并发操作的优化也进行了很多,因此在高并发场景下表

现出色。它还提供了一些特别的方法(如迭代器和批量操作等),可以更好地支持并发操

作。除了上述所提到的方法,ConcurrentHashMap还提供了一些其他的方法,用于更方便

地进行并发操作。下面我们来看一下其中的几个方法。

1. replace方法

replace方法用于替换ConcurrentHashMap中指定键的值,只有在键存在的情况下,

才会执行替换操作。该方法的定义如下:

```

public V replace(K key, V value) {

Segment s;

HashEntry[] tab;

int hash = hash(de());

int j = (hash >>> segmentShift) & segmentMask;

if ((s = (Segment)segments[j]) != null && (tab = ) != null)

{

for (HashEntry e = (HashEntry)tab[( - 1) & hash];

e != null; e = ) {

K k;

if ((k = ) == key || ( == hash && (k))) {

V oldValue = ;

= value;

return oldValue;

}

}

}

return null;

}

```

与get和put方法类似,replace方法也是通过键计算出它所属的小片段,然后在小片

段中查找对应的键值对。如果找到了指定的键,并且该键的值不是null,则将其值替换成

新值。最后返回旧值。

```

public V remove(Object key) {

int hash = hash(de());

return segmentFor(hash).remove(key, hash, null);

}

```

3. size方法

```

public int size() {

long sum = 0;

for (Segment segment : segments) {

if (segment != null) {

sum += nt();

}

}

return sum >= _VALUE ? _VALUE : (int)sum;

}

```

与其他方法不同,size方法遍历了所有的小片段,并将它们的大小相加,得到了总的

键值对数目。

需要注意的是,由于小片段是通过分割数组来实现的,因此它们的大小可能并不相等,

而且随着键值对的添加和移除,它们的大小也会发生变化。在计算ConcurrentHashMap的

大小时,需要先逐个统计各个小片段的大小,然后再将它们加起来。

总结:

ConcurrentHashMap是Java中的一个线程安全的哈希表,通过将一个数组分割成多个

小的数组片段,每个片段都有一个独立的锁来控制对于该片段的访问,从而实现了高并发

下的线程安全。它还提供了一些优化和特别的方法(如弱一致性的迭代器和批量操作等),

以支持并发操作。ConcurrentHashMap在性能和扩展性等方面也有非常好的表现,可以应

对很多高并发场景。


发布者:admin,转转请注明出处:http://www.yc00.com/news/1710038312a1689826.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信