标签:表结构 value 相加 ext tom 高效 class 原则 pat
此文转载自:https://blog.csdn.net/qq_46153765/article/details/111770649#commentBox在多线程环境下,使用HashMap进行put操作会引起死循环,导致CPU利用率接近100%
死循环案例:
final HashMap<String, String> map = new HashMap<String, String>(2);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
map.put(UUID.randomUUID().toString(), "");
}
}, "ftf" + i).start();
}
}
}, "ftf");
t.start();
t.join();
原因:多线程会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获取Entry
ConcurrentHashMap使用Wang/Jenkins hash的变种算法对元素的hashCode进行一次再散列:
private static int hash(int h) {
h += (h << 15) ^ 0xffffcd7d;
h ^= (h >>> 10);
h += (h << 3);
h ^= (h >>> 6);
h += (h << 2) + (h << 14);
return h ^ (h >>> 16);
}
Segment的get操作实现非常简单高效。先经过一个再散列,然后使用这个散列值通过散列运算定位到Segment,再通过散列算法定位到元素
public V get(Object key) {
int hash = hash(key.hashCode());
return segmentFor(hash).get(key, hash);
}
ConcurrentHashMap的get操作相比于HashTable容器的get方法不需要加锁,除非读到的值是空才会加锁重读。它是如何做到的呢? 原因是它的get方法里将要使用的共享变量都定义成volatile类型,如用于统计当前Segement大小的count字段和用于储存值的HashEntry的value。定义成volatile的变量,能够在线程之间保持可见性,保证多个线程读到的是最新的值,但是只能被单线程写(有一种情况可以被多线程写,就是写入的值不依赖与原值)。根据Java内存模型的happen before原则,对volatile字段的写入操作先于读操作,即使两个线程同时修改和获取volatile变量,get操作也能拿到最新的值
transient volatile int count;
volatile V value;
参考:《Java并发编程的艺术》仅供个人学习,不可用于商业用途,如有侵权,立删
标签:表结构 value 相加 ext tom 高效 class 原则 pat
原文地址:https://www.cnblogs.com/phyger/p/14205100.html