hashmap线程不安全问题_arraylist线程不安全体现在哪里

hashmap线程不安全问题_arraylist线程不安全体现在哪里一、头插法导致死循环在jdk1.7以前,HashMap在进行扩容时采用的是头插法,可能当时别人觉得这样比较高效,但是也带来了线程安全问题。刚开始时HashMap是这样的:正常扩容后是这样的:但如果是在多线程下,两个线程的指向3:此时线程1比线程2先执行,那么线程1就会指向7,将线程7.next指向了3,:但是对于线程2来说,3.next=7;所以就形成了死循环,也就是3和7构成了环。二、数据覆盖在jdk1.8以后,改了1.7以前的小毛病,但是新的问题又来了,我们来看下源码:

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、头插法导致死循环

在jdk1.7以前,HashMap在进行扩容时采用的是头插法,可能当时别人觉得这样比较高效,但是也带来了线程安全问题。
刚开始时HashMap是这样的:
在这里插入图片描述
正常扩容后是这样的:
在这里插入图片描述
但如果是在多线程下,两个线程的指向3:
在这里插入图片描述
此时线程1比线程2先执行,那么线程1就会指向7,将线程7.next指向了3,:
在这里插入图片描述
但是对于线程2来说,3.next=7;所以就形成了死循环,也就是3和7构成了环。

二、数据覆盖

在jdk1.8以后,改了1.7以前的小毛病,但是新的问题又来了,我们来看下源码:

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) { 
   
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
            //问题出在这里
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else { 
   
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else { 
   
                for (int binCount = 0; ; ++binCount) { 
   
                    if ((e = p.next) == null) { 
   
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { 
    // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

这是HashMap的put方法,会出现线程不安全的代码起源我已经标出。比如现在有两个线程都要调用put方法,都进行了判断,且都满足条件可以直接插入,这时线程1先插入,线程2在执行的时候就不会再次进行判断,也是直接插入,这就出现了元素覆盖,也就是说线程1做了无用功。

三、线程安全的字典

那么HashMap是线程不安全的,我们在多线程的场景下可以使用线程安全的字典:

3.1Hashtable

这个类相当于是在主要的方法前加了synchronized修饰,所以效率会非常低,通常不推荐使用

3.2ConcurrentHashMap

ConcurrentHashMap减小了锁的离度,在链表的头结点加锁,效率相对高一些。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/183784.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • pstack脚本_pstack

    pstack脚本_pstack脚本写入shell文件pstack,直接执行这个脚本pstackpid#!/bin/shiftest$#-ne1;thenecho”Usage:`basename$0.sh`<process-id>”1>&2exit1fiiftest!-r/proc/$1;thenecho”Process$1notfound.”1>&2exit1fi#GDBdoe…

    2025年11月15日
    3
  • Struts2 拦截器的Interceptor接口与AbstractInterceptor抽象类

    Struts2 拦截器的Interceptor接口与AbstractInterceptor抽象类在学习Struts2框架的时候,遇到了拦截器,一开始拦截器都应该之间或者间接去实现Interceptor接口的,但是后来看到了AbstractInterceptor这个抽象类。之前学过的JAVA知识有点忘记了,所以特意查了一下关于抽象类和接口之间的关系。我们不想使用Interceptor接口去实现拦截器对象,是因为这个接口中有3个方法,但是我们在实际上我们主要用到的就是只有intercept()这

    2022年5月15日
    45
  • uri与url的区别简单理解(uri和url有什么区别)

    一、URI<1>什么是URIURI,通一资源标志符(UniformResourceIdentifier,URI),表示的是web上每一种可用的资源,如HTML文档、图像、视频片段、程序等都由一个URI进行定位的。<2>URI的结构组成URI通常由三部分组成:①访问资源的命名机制;②存放资源的主机名;③资源自身的名称。<3>…

    2022年4月14日
    62
  • django 用户注册_云端注册用户怎么注册

    django 用户注册_云端注册用户怎么注册前言我们使用django创建用户可以使用注册接口的方式,也可以使用django自带的后台管理系统,这里就介绍使用后台管理系统创建用户admin后台管理系统在使用之前我们可以使用第三方的插件,来美

    2022年7月29日
    9
  • python进制转换代码_python十六进制转换成十进制

    python进制转换代码_python十六进制转换成十进制本文实例讲述了Python实现的十进制小数与二进制小数相互转换功能。分享给大家供大家参考,具体如下:十进制小数⇒二进制小数乘2取整对十进制小数乘2得到的整数部分和小数部分,整数部分即是相应的二进制数码,再用2乘小数部分(之前乘后得到新的小数部分),又得到整数和小数部分。如此不断重复,直到小数部分为0或达到精度要求为止.第一次所得到为最高位,最后一次得到为最低位如:0.25的二进制0.25*2=…

    2025年11月30日
    6
  • SqlTransaction 数据库编程事务使用示例

    SqlTransaction 数据库编程事务使用示例在提交或回滚SqlTransaction时,应始终使用Try/Catch进行异常处理。如果连接终止或事务已在服务器上回滚,则Commit和Rollback都会生成InvalidOperationException。 下面的示例创建一个SqlConnection和一个SqlTransaction。此示例还演示如何使用BeginTransaction、Commit和…

    2022年5月23日
    32

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号