我们知道,全局变量的值可以随时被其他线程修改,但是为什么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的深副本吗?
3条答案
按热度按时间hk8txs481#
那么,为什么“让”构式中的“它”不能受到其他线索的影响呢?
.let { }
没有什么神奇的,它是一个非常简单的函数,当你写demo?.let { println(it) }
时,代码实际上等价于:正如您所看到的,创建了一个新变量,并将其值设置为
demo
,因此,如果其他线程修改demo
,it
不会受到影响。这是对象“演示”的深层副本?
这真的 * 不是 * 一个深副本:如果一个线程改变了
Demo
示例的属性,那么任何访问该示例 * 的代码都可以看到这些属性,即使是通过it
变量。但是,您不需要深层副本就可以让这段代码正常工作。
demo
是一个引用变量,它最初指向在代码开始时创建的Demo()
示例。当我们执行
val it = demo
时(来自?.let
的等效代码),我们将demo
变量的当前值复制到it
中,该值是Demo
示例的 reference,我们说it
“指向”与demo
相同的Demo
示例。当另一个线程将
demo
设置为null
时,它不会改变Demo
示例中的任何内容,其效果是,现在demo
不再指向初始的Demo
示例,但it
仍然指向它。(* 尽管这里有一些微妙之处,比如happen-before关系、volatile变量等,但我们不要把事情复杂化)
yjghlzjz2#
另一个补充点有助于你的理解。
没有类示例变为空的概念。如果你正在使用一个示例,它就存在并且不为空。
引用可以为空。您可以对同一示例进行多个引用。只要某个地方至少存在一个对类示例的引用,则该类示例仍然存在,即使以前指向它的一些引用现在指向其他对象,如
null
。如果你这样做,你不是在复制你的类示例,更不是在深度复制它!你只是在复制引用。
如果将
demo
更改为指向null
,则这对Demo示例本身没有影响,并且item
仍然指向同一个未更改的示例。由于
.let
以相同的方式在内部复制引用,因此即使demo
属性更改为指向其他对象或null
,它也会保留该引用。bfrts1fy3#
它不是一个深度拷贝,它就是原来的对象,你需要意识到的是变量只是指向对象的指针,假设你有
你有4个变量指向同一个对象。如果你
在这4行之后,您只需使该变量不再指向该对象。
demo2
,demo3
和demo4
仍将指向您一开始指定给demo
的对象。同样的情况也发生在let
块中,其中it
只是另一个变量,它在块的开头被赋予了相同的对象,因此对原始demo
的赋值不会影响it