我们正在try 存储特定密钥的唯一对象.当在多线程环境中调用getMyObject时,我们在返回语句时得到null ptr异常

object SampleClass
{
 
    fun getMyObject(Id : String) : MyObject
    {
        if(!myMap.containsKey(Id))
        {
            synchronized(SampleClass)
            {
                if(!myMap.containsKey(Id))
                {
                    myMap[Id] = MyObject()
                }
            }
        }
        return myMap[Id]!!
    }

    private val myMap = HashMap<String,MyObject>()
}

似乎即使contains方法在try 获取值时返回true,该值也返回null.

推荐答案

如果你试图智取内存模型,就会遇到这种麻烦.如果查看HashMap的源代码,您会发现containsKey实现为:

public boolean containsKey(Object key) {
  return getNode(key) != null;
}

请注意,只有当给定键对应HashMap.Node个对象时,它才会返回true.现在,get是如何实现的:

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(key)) == null ? null : e.value;
}

你看到的是unsafe publication问题的一个例子.假设2个线程(A和B)调用getMyObject以获取不存在的密钥.A略高于B,所以在B调用containsKey之前,它进入synchronized块.特别地,A在B呼叫containsKey之前呼叫put.对put的调用创建了一个新的Node对象,并将其放入哈希映射的内部数据 struct 中.

现在,考虑B在A存在synchronized块之前调用containsKey的情况.B might查看A放置的Node对象,在这种情况下,containsKey返回true.然而,此时 node 被安全地发布,因为它是由B以非同步方式并发访问的.无法保证其构造函数(设置其value字段的构造函数)已被调用.即使调用了它,也不能保证value引用(或构造函数设置的任何引用)与 node 引用一起发布.这意味着B可以看到一个不完整的 node : node 引用,但不能看到其值或任何字段.当B前进到get时,它读取null作为未安全发布的 node 的值.因此,NullPointerException.

下面是一个特殊的图表,用于可视化:

         Thread A                                     Thread B
- Enter the synchronized block
  - Call hashMap.put(...)
    - Insert a new Node
                                           - See the newly inserted (but not yet 
                                             initialized from the perspective of B)
                                             Node in HashMap.containsKey
                                           - Return node.value (still null)
                                             from HashMap.get
                                           - !! throws a `NullPointerException`
    ...
- Exit the synchronized block
  (now the node is safely published)

以上只是一个可能出错的场景(见注释).为避免此类危险,请使用ConcurrentHashMap(例如map.computeIfAbsent(key, key -> new MyObject()))或不要在synchronized街区外同时访问HashMap.

Kotlin相关问答推荐

Microronaut Data 4和JDbi

有没有一种简单的方法来识别物体?

列表在 android WebView 中没有正确迭代

MyType.()在 Kotlin 中是什么意思?

如何在 Android Jetpack Compose 中的画布上绘制一侧加厚的描边?

如何限制 Kotlin 中的枚举?

Android数据绑定在自定义视图中注入ViewModel

如何禁用智能投射突出显示 Kotlin?

Kotlin 静态函数:伴生对象,@JvmStatic @JvmField

为什么我在使用 Jetpack Compose clickable-Modifier 时收到后端内部错误:Exception during IR lowering error?

如何从定义它们的类外部调用扩展方法?

Kotlin 类的调用方法

Kotlin:如何在活页夹中返回正在运行的服务实例?

片段内的 Kotlin 按钮 onClickListener 事件

@uncheckedVariance 在 Kotlin 中?

Kotlin JVM 和 Kotlin Native 有什么区别?

指定为非null的参数在ArrayAdaper中为null

如何在Kotlin中使用Handler和handleMessage?

如何在不绑定ViewModel(MVVM)中的UI的情况下使用android导航?

var str:String是可变的还是不可变的?