zoukankan      html  css  js  c++  java
  • HashMap非线程安全分析

    通过各方资料了解,HashMap不是线程安全的,但是为什么不是线程安全的,在什么情况下会出现问题呢?

    1. 下面对HashMap做一个实验,两个线程,并发写入不同的值,key和value相同,最后再看看value和key是否相等。

    import java.util.HashMap;
    
    public class TestHashMap {
    
        public static final HashMap<String, String> hashMap = new HashMap<String, String>();
    
        public static void main(String[] args) throws InterruptedException {
    
            // 线程一
            Thread t1 = new Thread() {
                public void run() {
                    for (int i = 0; i < 25; i++) {
                        hashMap.put(String.valueOf(i), String.valueOf(i));
                    }
                }
            };
    
            // 线程二
            Thread t2 = new Thread() {
                public void run() {
                    for (int j = 25; j < 50; j++) {
                        hashMap.put(String.valueOf(j), String.valueOf(j));
                    }
                }
            };
    
            t1.start();
            t2.start();
    
            // 主线程休眠1秒钟,以便t1和t2两个线程将firstHashMap填装完毕。
            Thread.sleep(1000);
    
            for (int i = 0; i < 50; i++) {
                // 如果key和value不同,说明在两个线程put的过程中出现异常。
                if (!String.valueOf(i).equals(hashMap.get(String.valueOf(i)))) {
                    System.err.println("出现多线程异常,序号:"+i);
                }
            }
        }
    }

    经过多次测试,都会出现类似下面的错误:

    出现多线程异常,序号:0

    出现多线程异常,序号:1

    2. 为什么会导致这样的情况

      1) 查看HashMap的put方法

      

     public V put(K key, V value) {
            if (key == null)
                return putForNullKey(value);
            int hash = hash(key.hashCode());
            int i = indexFor(hash, table.length);
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
    
            modCount++;
            addEntry(hash, key, value, i);
            return null;
        }

    因为put方法没有加synchronized方法,在执行modCount++,addEntry时都有可能出现问题,modCount代码比较简单,就不深究了,下面再看看addEntry函数。

      2)addEntry函数

        void addEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
            table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
            if (size++ >= threshold)
                resize(2 * table.length);
        }

    当容量达到阈值时,就会对map进行扩容,然后将原有值拷贝到新的值中;可以想象,如果两个线程同时对map进行扩容,将会带来巨大的问题,如数据丢失。

    基于这点原因考虑,如果map本身的大小就比较大,不会扩容,那情况如何?

    修改上述代码样例中的hashMap的构造函数,带上初始大小

    public static final HashMap<String, String> hashMap = new HashMap<String, String>(50);

    再次测试,基本没有出现并发的问题。

    不过理论上看应该还是有问题的,只是出现的几率减小了,对于多线程情况下,可以使用HashTable或者ConcurrentHashMap类。

      

     参考资料: 

    http://blog.sina.com.cn/s/blog_4a1f59bf0100o98k.html
    http://www.iteye.com/topic/656670

  • 相关阅读:
    VK Cup 2012 Qualification Round 1 C. Cd and pwd commands 模拟
    VK Cup 2015
    DP总结 ——QPH
    Codeforces Round #244 (Div. 2) B. Prison Transfer 线段树rmq
    Codeforces Round #311 (Div. 2) E. Ann and Half-Palindrome 字典树/半回文串
    Codeforces Round #311 (Div. 2) D. Vitaly and Cycle 图论
    Codeforces Round #311 (Div. 2) C. Arthur and Table Multiset
    Codeforces Round #311 (Div. 2)B. Pasha and Tea 水题
    Codeforces Round #311 (Div. 2) A. Ilya and Diplomas 水题
    Codeforces Round #260 (Div. 1) D. Serega and Fun 分块
  • 原文地址:https://www.cnblogs.com/jerry1999/p/3676612.html
Copyright © 2011-2022 走看看