美文网首页
局部变量保证线程安全

局部变量保证线程安全

作者: 风火1989 | 来源:发表于2020-04-16 20:10 被阅读0次

局部变量保证线程安全

首先来看String这个类的hashcode方法,如下

public int hashCode()
{
    int h = hash; /* 代码① */
    if ( h == 0 && value.length > 0 )
    {
        char val[] = value;
 
        for ( int i = 0; i < value.length; i++ )
        {
            h = 31 * h + val[i];
        }
        hash = h;       /* 代码② */
    }
    return(h);              /* 代码③ */
}

hashString类的一个属性,可以看到这边首先是代码①读取了本地属性的值,并且赋值给局部变量h。并且使用h进行了业务逻辑的判断。如果h没有值的话,就进行 Hash 值的生成,并且赋值到h上,并且在代码②处赋值给了属性hash。最终返回的,也是局部变量h的值。那么上述的代码能否修改为下面的模式

public int hashCode()
{
    if ( hash == 0 && value.length > 0 )  /* 代码① */
    {
        char    val[]    = value;
        int    h    = 0;
        for ( int i = 0; i < value.length; i++ )
        {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return(hash); /* 代码② */
}

修改的代码没有局部变量,直接使用属性本身来操作。

答案是否定的,因为这种写法是线程不安全的,可能导致方法的返回值是 0 。似乎有点费解,因为如果hash值为0 ,则代码会进入循环体,对hash值进行更新。所以乍看之下,无论如何是不会返回 0 的。

上述的理解逻辑,在单线程环境下,是正确的。但是这段代码工作在多线程环境。实际上,上述代码有两次对hash值的读取,分别是代码①和②。可能会出现一种情况,在代码①处,读取到hash值不为 0 ,在代码②处,读取到hash值为0,并且以此为结果返回了。显然此时这种结果是错误的。

要理解这种场景的发生需要从 JMM 的规则谈起。首先,两个读取之间是没有因果关系的,因此不存在第一个对变量的读取观察到了值,第二个对该变量的读取也要观察到这个值。其次,在 JMM 中,对一个变量的读取操作允许其观察最后一次到对该变量的写入,只要没有 HB 关系来阻止这个读取的观察效果。此外,对象属性的默认值也是由写入动作触发的。这意味着对hash值的写入有两个地方,一个在于对象构造时,一个在于其他线程对hash值的写入。由于这两个写入没有 HB 关系,因此对hash的读取可能读取到任意一个写入的结果。所以,可能会出现的情况是在代码①处读取到了其他线程对hash值的写入,因此跳过了内部的写入逻辑。而在代码②处再次读取hash值,此时读取到了对象构造时对hash默认值的写入,导致返回 0 。

从 JMM 规则角度是最正确的理解,但是为了形象的想象这一切如何发生,我们可以将上面的程序修改如下

public int hashCode()
{
    int a = hash;
    if ( hash == 0 && value.length > 0 ) /* 代码① */
    {
        char    val[]    = value;
        int    h    = 0;
        for ( int i = 0; i < value.length; i++ )
        {
            h = 31 * h + val[i];
        }
        a = hash = h;
    }
    return(a); /* 代码② */
}

实际上,这的确是在执行代码逻辑的时候,一种可能的代码重排序变种。假定一开始hash值为0,则a为 0 。在if判断的时候,hash读取到了其他线程写入的值,因此没有执行计算逻辑,最终返回了a的值,也就是 0 。

相关文章

  • 局部变量保证线程安全

    局部变量保证线程安全 首先来看String这个类的hashcode方法,如下 hash是String类的一个属性,...

  • Java线程安全(volatile & synchron

    总结 volatile不能保证线程安全而synchronized可以保证线程安全。volatile只能保证被其修饰...

  • java 栈帧

    每个线程都维护有自己的栈,每一个方法的执行都代表入栈和出栈,该结构保证了局部变量的线程安全性

  • 【高并发】面试官问我:为什么局部变量是线程安全的?

    写在前面 相信很多小伙伴都知道局部变量是线程安全的,那你知道为什么局部变量是线程安全的吗? 前言 多个线程同时访问...

  • 线程安全的NSMutableDictionary

    NSDictionary是线程安全的,NSMutableDictionary是线程不安全的。利用锁来保证线程的安全...

  • 2021-08-20

    用什么Map可以保证线程安全,为什么?ConcurrentHashMap为什么能保证线程安全?1.7和1.8原理有...

  • ArrayList 和 linkedList 异同

    是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全。 底层数据结...

  • Android-ThreadLocal

    ThreadLocal简介 ThreadLocal是线程内部的局部变量,保证该变量在线程内部是独立的,我们可以通过...

  • ThreadLocal探究

    什么是ThreadLocal ThreadLoacl提供了可以线程封闭的变量存储,提供线程内的局部变量,可以保证在...

  • Java 之 ThreadLocal 详解

    1. 概念 ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变...

网友评论

      本文标题:局部变量保证线程安全

      本文链接:https://www.haomeiwen.com/subject/maodvhtx.html