• HashMap在高并发下如果没有处理线程安全会有怎样的安全隐患,具体表现是什么


    Hashmap在并发环境下,可能出现的问题:
    1、多线程put时可能会导致get无限循环,具体表现为CPU使用率100%;
    原因:在向HashMap put元素时,会检查HashMap的容量是否足够,如果不足,则会新建一个比原来容量大两倍的Hash表,然后把数组从老的Hash表中迁移到新的Hash表中,迁移的过程就是一个rehash()的过程,多个线程同时操作就有可能会形成循环链表,所以在使用get()时,就会出现Infinite Loop的情况

    // tranfer()片段
    // 这是在resize()中调用的方法,resize()就是HashMap扩容的方法     
    for (int j = 0; j < src.length; j++) {
        Entry e = src[j];
        if (e != null) {
         src[j] = null;
         do {
         Entry next = e.next;  //假设线程1停留在这里就挂起了,线程2登场
         int i = indexFor(e.hash, newCapacity);
         e.next = newTable[i];
         newTable[i] = e;
         e = next;
         } while (e != null);
       }
    }
    

    线程一:Thread1影响key1

    线程二:Thread1影响key2

    因为两条线程的影响,倒是出现循环的情况

    出现问题的测试代码:

    import java.util.HashMap;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class MyThread extends Thread {
        /**
         * 类的静态变量是各个实例共享的,因此并发的执行此线程一直在操作这两个变量
         * 选择AtomicInteger避免可能的int++并发问题
         */
        private static AtomicInteger ai = new AtomicInteger(0);
        //初始化一个table长度为1的哈希表
        private static HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(1);
        //如果使用ConcurrentHashMap,不会出现类似的问题
    //       private static ConcurrentHashMap<Integer, Integer> map = new ConcurrentHashMap<Integer, Integer>(1);
    
        public void run() {
            while (ai.get() < 100000) {  //不断自增
                map.put(ai.get(), ai.get());
                ai.incrementAndGet();
            }
    
            System.out.println(Thread.currentThread().getName() + "线程即将结束");
        }
    
        public static void main(String[] args) {
            MyThread t0 = new MyThread();
            MyThread t1 = new MyThread();
            MyThread t2 = new MyThread();
            MyThread t3 = new MyThread();
            MyThread t4 = new MyThread();
            MyThread t5 = new MyThread();
            MyThread t6 = new MyThread();
            MyThread t7 = new MyThread();
            MyThread t8 = new MyThread();
            MyThread t9 = new MyThread();
    
            t0.start();
            t1.start();
            t2.start();
            t3.start();
            t4.start();
            t5.start();
            t6.start();
            t7.start();
            t8.start();
            t9.start();
    
        }
    }
    

    2、多线程put时可能导致元素丢失
    原因:当多个线程同时执行addEntry(hash,key ,value,i)时,如果产生哈希碰撞,导致两个线程得到同样的bucketIndex去存储,就可能会发生元素覆盖丢失的情况

    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);
        }
    

    建议:
    使用Hashtable 类,Hashtable 是线程安全的;
    使用并发包下的java.util.concurrent.ConcurrentHashMap,ConcurrentHashMap实现了更高级的线程安全;
    或者使用synchronizedMap() 同步方法包装 HashMap object,得到线程安全的Map,并在此Map上进行操作。

  • 相关阅读:
    MySql清空所有表数据【慎用】
    积分墙已死?积分墙冲榜失效?请看看别人怎么玩转积分墙!
    [转]小心PHP的类定义顺序与继承的问题
    [转]PHP中替换换行符
    [转]php的public、protected、private三种访问控制模式的区别
    [转]Zend Studio中将tab转换为4个空格
    PHP预定义常量
    PHP Unit资料收集
    『Python』setup.py简介
    『计算机视觉』Mask-RCNN_项目文档翻译
  • 原文地址:https://www.cnblogs.com/androidsuperman/p/9008243.html
Copyright © 2020-2023  润新知