kotlin 为什么?.let可以防止多线程中的nullPointException?

fhity93d  于 2023-01-13  发布在  Kotlin
关注(0)|答案(3)|浏览(148)

我们知道,全局变量的值可以随时被其他线程修改,但是为什么Kotlin中的?.let可以防止多线程中的NPE呢?例如:

var demo : Demo? = null
    demo = Demo()
    Thread{
        demo?.let {
            while (true){
                it.run()
                Thread.sleep(300)
            }
        }
    }.start()
    Thread.sleep(3000)
    demo = null

在上面的程序中,虽然主线程已经将变量demo设置为空,但是子线程可以正常执行。
那么,为什么let构造中的it不能受其他线程的影响呢?它是对象demo的深副本吗?

hk8txs48

hk8txs481#

那么,为什么“让”构式中的“它”不能受到其他线索的影响呢?
.let { }没有什么神奇的,它是一个非常简单的函数,当你写demo?.let { println(it) }时,代码实际上等价于:

val it = demo
if (it != null) {
    println(it)
}

正如您所看到的,创建了一个新变量,并将其值设置为demo,因此,如果其他线程修改demoit不会受到影响。
这是对象“演示”的深层副本?
这真的 * 不是 * 一个深副本:如果一个线程改变了Demo示例的属性,那么任何访问该示例 * 的代码都可以看到这些属性,即使是通过it变量。
但是,您不需要深层副本就可以让这段代码正常工作。demo是一个引用变量,它最初指向在代码开始时创建的Demo()示例。
当我们执行val it = demo时(来自?.let的等效代码),我们将demo变量的当前值复制到it中,该值是Demo示例的 reference,我们说it“指向”与demo相同的Demo示例。
当另一个线程将demo设置为null时,它不会改变Demo示例中的任何内容,其效果是,现在demo不再指向初始的Demo示例,但it仍然指向它。
(* 尽管这里有一些微妙之处,比如happen-before关系、volatile变量等,但我们不要把事情复杂化)

yjghlzjz

yjghlzjz2#

另一个补充点有助于你的理解。
没有类示例变为空的概念。如果你正在使用一个示例,它就存在并且不为空。

引用可以为空。您可以对同一示例进行多个引用。只要某个地方至少存在一个对类示例的引用,则该类示例仍然存在,即使以前指向它的一些引用现在指向其他对象,如null

如果你这样做,你不是在复制你的类示例,更不是在深度复制它!你只是在复制引用

val item = demo

如果将demo更改为指向null,则这对Demo示例本身没有影响,并且item仍然指向同一个未更改的示例。
由于.let以相同的方式在内部复制引用,因此即使demo属性更改为指向其他对象或null,它也会保留该引用。

bfrts1fy

bfrts1fy3#

它不是一个深度拷贝,它就是原来的对象,你需要意识到的是变量只是指向对象的指针,假设你有

var demo : Demo? = Demo()
var demo2 : Demo? = demo
var demo3 : Demo? = demo
var demo4 : Demo? = demo

你有4个变量指向同一个对象。如果你

demo = null

在这4行之后,您只需使该变量不再指向该对象。demo2demo3demo4仍将指向您一开始指定给demo的对象。同样的情况也发生在let块中,其中it只是另一个变量,它在块的开头被赋予了相同的对象,因此对原始demo的赋值不会影响it

相关问题