2、HashMap线程安全问题
1、并发测试,会出现的赋值成功,但是结果却是数量少了。
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
map.put("aaa_"+i, "value_aaa_"+i);
System.out.println(Thread.currentThread().getName() + "put");
}
}
},"aaa").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
map.put("bbb_"+i, "value_bbb_"+i);
System.out.println(Thread.currentThread().getName() + "put");
}
}
},"bbb").start();;
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
map.put("ccc_"+i, "value_ccc_"+i);
System.out.println(Thread.currentThread().getName() + "put");
}
}
},"ccc").start();;
Set<Entry<String,Object>> entrySet = map.entrySet(); // 怎么做到返回所有的映射关系
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int size = entrySet.size();
System.out.println(size); // 映射的数目才是hashmap的存放的键值对
// 打印结果,会出现size小于3000的情况。
hashmap就是共享资源
分析:
多线程访问,在创建entry时候,拿到的Entry<K,V> e = table[i] ,那么再new Entry的时候就会后一个线程覆盖上一个线程的值。
(原理:多个线程同时发生碰撞的时候,且都在数组的同一个位置时。同时对链表的头部更新操作,出现值被覆盖)
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}
2、线程不安全造成环状链表分析
void transfer(Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
for (Entry<K,V> e : table) {
while(null != e) {
Entry<K,V> next = e.next; // 拿到next,保存旧链表next对象
if (rehash) {
e.hash = null == e.key ? 0 : hash(e.key);
}
int i = indexFor(e.hash, newCapacity); // 计算下标
e.next = newTable[i]; //newTable[i]的值赋给e.next (第一次table[i]是null值)
newTable[i] = e; // 再把e赋给newTable[i]
每次循环都是将新来的e指向已经存在的对象newTable[i]处的链表,再将此链表赋给newTable[i]。这样做和put原理一样,复杂度是O(1)。
e = next;
}