调试JDK源码-Hashtable实现原理以及线程安全的原因

本文通过调试JDK源码,详细剖析Hashtable实现原理,揭示其线程安全的原因。从初始化、put操作流程,到哈希冲突处理,展示了Hashtable如何通过synchronized保证并发环境下的一致性。
摘要由CSDN通过智能技术生成

调试JDK源码-一步一步看HashMap怎么Hash和扩容

调试JDK源码-ConcurrentHashMap实现原理

调试JDK源码-HashSet实现原理

调试JDK源码-调试JDK源码-Hashtable实现原理以及线程安全的原因

 

Hashtable是线程安全的,我们从源码来分析

 

代码很简单

Hashtable<String, String> ht = new Hashtable<String, String>();
        ht.put("111", "http://blog.csdn.net/unix21/");
        ht.put("222", "www.csdn.net");
        ht.put("333", <a target=_blank href="http://www.java.com">www.java.com</a>);

 

注释写的很好,生成一个新的,空的hashtable,使用默认的capacity容量为11,factor增长因子为0.75

 

这一步开始初始化

下一步就完成了实例化

 

Entry是一个静态嵌套类

    /**
     * Hashtable bucket collision list entry
     */
    private static class Entry<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Entry<K,V> next;

        protected Entry(int hash, K key, V value, Entry<K,V> next) {
            this.hash = hash;
            this.key =  key;
            this.value = value;
            this.next = next;
        }

        @SuppressWarnings("unchecked")
        protected Object clone() {
            return new Entry<>(hash, key, value,
                                  (next==null ? null : (Entry<K,V>) next.clone()));
        }

        // Map.Entry Ops

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            if (value == null)
                throw new NullPointerException();

            V oldValue = this.value;
            this.value = value;
            return oldValue;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<?,?> e = (Map.Entry<?,?>)o;

            return (key==null ? e.getKey()==null : key.equals(e.getKey())) &&
               (value==null ? e.getValue()==null : value.equals(e.getValue()));
        }

        public int hashCode() {
            return hash ^ Objects.hashCode(value);
        }

        public String toString() {
            return key.toString()+"="+value.toString();
        }
    }

 

其实就是因为这个put方法是synchronized的所以可以保证其线程安全
 

 

算出hash码,然后算出在默认长度11下的index索引是4

 

默认entry为空直接跳到addEntry(hash, key, value, index)

 

addEntry方法体内,一开始modCount=0

 

下一步由于count =0  >  threshold=8

 

生成一个新的Entry

 

新的节点挂在tab[4]上,然后count++。

 

回到put函数,至此一个完整的put就完成了。

 

第二次put,生成的index是7

 

哈希冲突

注意这段代码,其实就是判断是否出现hash冲突了

Entry<K,V> entry = (Entry<K,V>)tab[index];
        for(; entry != null ; entry = entry.next) {
            if ((entry.hash == hash) && entry.key.equals(key)) {
                V old = entry.value;
                entry.value = value;
                return old;
            }
        }


 知道了hashtable默认的长度是11,那么我们就可以制造一个会产生哈希冲突的数据集就可以了

Hashtable<String, String> ht = new Hashtable<String, String>();
        for (int i = 0; i < 20; i++) {
            ht.put((char) (i + 65) + (char) (i + 66) + (char) (i + 67) + "", i + ">>>http://blog.csdn.net/unix21/");
        }

 

经过几次put之后,终于产生了一次哈希冲突,这次的索引是10,而tab[10]已经有值。

产生冲突有2个原因一个是因为相同的key必然会产生相同的索引。还有一种就是不同的值产生哈希冲突了。

所以这里先判断是不是相同的key导致的。

 

进入addEntry方法

 

之前的冲突节点一并传入,被挂在新节点后面也就是其next下

 

回到addEntry,可以看到新的节点以及挂在tab[10].next下了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值