您现在的位置是:主页 > news > wordpress启动慢/河南网站排名优化
wordpress启动慢/河南网站排名优化
admin2025/4/27 7:11:19【news】
简介wordpress启动慢,河南网站排名优化,怎么使用网站模板,奶茶加盟网站建设公司哪家好HashMap为什么不是线程安全,并发操作Hashmap会带来什么问题: 这个问题曾经有一个面试官问过我,当时我天真的以为是读写操作并发时存在脏数据的问题,当时面试官不置可否。我后面回来查资料,发现没有那么简单。并发操作H…
HashMap为什么不是线程安全,并发操作Hashmap会带来什么问题:
这个问题曾经有一个面试官问过我,当时我天真的以为是读写操作并发时存在脏数据的问题,当时面试官不置可否。我后面回来查资料,发现没有那么简单。并发操作HashMap,是有可能带来死循环以及数据丢失的问题的。
具体情况如下:(以下代码转自美团点评技术团队的文章Java8系列之重新认识HashMap)
情景如下代码:
public class HashMapInfiniteLoop { private static HashMap<Integer,String> map = new HashMap<Integer,String>(2,0.75f); public static void main(String[] args) { map.put(5, "C"); new Thread("Thread1") { public void run() { map.put(7, "B"); System.out.println(map); }; }.start(); new Thread("Thread2") { public void run() { map.put(3, "A); System.out.println(map); }; }.start(); } }
其中,map初始化为一个长度为2的数组,loadFactor=0.75,threshold=2*0.75=1,也就是说当put第二个key的时候,map就需要进行扩容。
考虑这样一种情况:
先放出transfer的部分代码:
do { Entry<K,V> next = e.next; //假设线程一执行到这里就被调度挂起了 int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null);
线程1、线程2都添加了数据之后,线程1执行到transfer()方法的第一行就被调度挂起了,这时线程2被调度来执行扩容操作。线程2的扩容操作结束之后,线程1被调度回来继续执行,此时由于线程2的执行,e已经指向了线程2修改之后的反转链表,但是线程1并不知道线程2已经在它之前做过这些操作了,于是它继续往下走,此时next=key(7),
然后计算索引。索引计算完之后执行e.next=newTable[i],此时e.next=key(7)。继续往下走,newTable[i]=e,此时newTable[i]=key(3),再往下,e=next,此时e指向了key(7),本次循环结束。从线程二重组链表结束,到线程1第一轮循环结束的变化图如下:
一切看起来都还没有什么问题。然后新一轮循环开始
这一轮循环我们不需要走完,就能发现问题。
第一句,执行后为:next=null;
第二句,计算索引,还是i
第三句,在这里就出问题了,这句话执行的是e.next=newTable[i],我们看上图,newTable[i]指向的是key(3),因此出现链表末尾的元素的next指针指向了链表头,循环链表就出现了。(按道理,HashMap是不存在循环链表的。)
第四句话,将链表头的元素换成key(7),而循环链表依然存在。
第五句,e=null,执行到这循环结束,因为e=null了。
整个过程并不会发生明显的异常。看起来一切安好。顺利的完成了rehash,但是悲剧在后面:当我们调用get()这个链表中不存在的元素的时候,就会出现死循环。go die
一句话总结就是,并发环境下的rehash过程可能会带来循环链表,导致死循环致使线程挂掉。
因此并发环境下,建议使用Java.util.concurrent包中的ConcurrentHashMap以保证线程安全。
至于HashTable,它并未使用分段锁,而是锁住整个数组,高并发环境下效率非常的低,会导致大量线程等待。
同样的,Synchronized关键字、Lock性能都不如分段锁实现的ConcurrentHashMap。