java基础 HashMap1.7与1.8

老生常谈java常面 HashMap:
首先都知道HashMap是线程不安全的那么聊一聊HashMap:

  • 默认初始化大小 16
  • 负载因子 0.75 即当存放数据数量达到当前预计数量的 0.75时 会发生扩容
  • 扩容倍数 2 即发生扩容会是之前预计数量的两倍

ok 基础说完了那就聊聊为什么会不安全
首先说jdk1.7的不安全
重点代码如下

  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;
                if (rehash) {
                    e.hash = null == e.key ? 0 : hash(e.key);
                }
                int i = indexFor(e.hash, newCapacity);
                e.next = newTable[i];
               newTable[i] = e;
                e = next;
            }
        }
    }
  • 死循环问题
    明确的是 1.7采用链表加数组的形式存储 而且如果存在hash碰撞它采用头插法存放数据
    上述 来重点看最后三行 赋值操作
    在对table进行扩容到newTable后,需要将原来数据转移到newTable中, 这里可以看出在转移元素的过程中,使用的是头插法,也就是链表的顺序会翻转,这里也是形成死循环的关键点。下面进行详细分析。
    假如线程A刚刚执行完 newTable[i] = e; 挂起
    而线程B执行完成了该操作那么就会出现 冲突的数据即 死循环

  • 扩容丢数据的问题
    同样线程A执行到 newTable[i] = e; 挂起
    此时线程B完成了 resize()操作
    此时切回线程A 执行 **e = next;**此时next会被B修改从而出现数据丢失并且有可能出现空值导致死循环出现

那么继续 jdk1.8:
首先还是重要代码

1final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                    boolean evict) {
 3         Node<K,V>[] tab; Node<K,V> p; int n, i;
 4if ((tab = table) == null || (n = tab.length) == 0)
 5             n = (tab = resize()).length;
 6if ((p = tab[i = (n - 1) & hash]) == null) // 如果没有hash碰撞则直接插入元素
 7             tab[i] = newNode(hash, key, value, null);
 8else {
 9             Node<K,V> e; K k;
10if (p.hash == hash &&
11                 ((k = p.key) == key || (key != null && key.equals(k))))
12                 e = p;
13elseif (p instanceof TreeNode)
14                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
15else {
16for (int binCount = 0; ; ++binCount) {
17if ((e = p.next) == null) {
18                         p.next = newNode(hash, key, value, null);
19if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
20                             treeifyBin(tab, hash);
21break;
22                     }
23if (e.hash == hash &&
24                         ((k = e.key) == key || (key != null && key.equals(k))))
25break;
26                     p = e;
27                 }
28             }
29if (e != null) { // existing mapping for key
30                 V oldValue = e.value;
31if (!onlyIfAbsent || oldValue == null)
32                     e.value = value;
33                 afterNodeAccess(e);
34return oldValue;
35             }
36         }
37         ++modCount;
38if (++size > threshold)
39             resize();
40         afterNodeInsertion(evict);
41returnnull;
42     }
  • 数据丢失问题

jdk1.8中HashMap中put操作的主函数, 注意第6行代码,如果没有hash碰撞则会直接插入元素。如果线程A和线程B同时进行put操作,刚好这两条不同的数据hash值一样,并且该位置数据为null,所以这线程A、B都会进入第6行代码中。假设一种情况,线程A进入后还未进行数据插入时挂起,而线程B正常执行,从而正常插入数据,然后线程A获取CPU时间片,此时线程A不用再进行hash判断了,问题出现:线程A会把线程B插入的数据给覆盖,发生线程不安全。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默海情深以往

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值