As we know, the value of a global variable can be modified by other threads at any time. However why can ?.let
in kotlin prevent an NPE in multithreading? For example:
var demo : Demo? = null
demo = Demo()
Thread{
demo?.let {
while (true){
it.run()
Thread.sleep(300)
}
}
}.start()
Thread.sleep(3000)
demo = null
In the above programme, the child thread can be executed normally, though the main thread has set the variable demo
to null.
So, why can't the 'it' in the construction of 'let' be influenced by other thread? Is it a deep copy of the Object 'demo'?
CodePudding user response:
So, why the 'it' in the construction of 'let' can not be influenced by other thread?
.let { }
has really nothing magical. It's a very simple function. When you write demo?.let { println(it) }
, the code is effectively equivalent to:
val it = demo
if (it != null) {
println(it)
}
So as you can see, a new variable is created and set to the value of demo
. Therefore, if other threads modify demo
, it
is not affected.
It's a deep copy of the Object 'demo'?
It's really not a deep copy: if a thread mutates the properties of the Demo
instance, they will be visible by any code that accesses that instance*, even through the it
variable.
However, you don't need a deep copy for this code to work fine. demo
is a reference variable that initially points to the Demo()
instance created at the beginning of the code.
When we do val it = demo
(from the code equivalent to ?.let
), we copy the current value of the demo
variable into it
, and that value is the reference to the Demo
instance. We say that it
"points to" the same Demo
instance as demo
.
When the other thread sets demo
to null
, it doesn't change anything in the Demo
instance. This has the effect that, now, demo
doesn't point anymore to the initial Demo
instance. But it
still points to it.
(*although there are some subtleties here, like happen-before relationships, volatile variables etc., but let's not complicate things)
CodePudding user response:
It's not a deep copy. It's literally the object that it was before. What you need to realize is that variables are just pointers to objects. Say you have
var demo : Demo? = Demo()
var demo2 : Demo? = demo
var demo3 : Demo? = demo
var demo4 : Demo? = demo
you have 4 variables pointing to the same object. If you do
demo = null
after these 4 lines then you merely make that variable no longer point to that object. demo2
, demo3
and demo4
still will point to the object that you assigned to demo
in the beginning. The same happens in the let
block where it
is just another variable that was assigned the same object in the beginning of the block so that any assignments to the original demo
won't have effect on it
CodePudding user response:
Another supplementary point to help with your understanding.
There is no concept of a class instance becoming null. If you are working with an instance, it exists and it is not null.
References can become null. You can have multiple references to the same instance. As long as there exists somewhere at least one reference to a class instance, that class instance still exists, even if some of the references that used to point to it are now pointing at something else, such as null
.
If you do this, you are not copying your class instance, and definitely not deep-copying it! You are only copying the reference.
val item = demo
If demo
is changed to point at null
, that has no effect on the instance of Demo itself, and item
is still pointing at that same, unchanged instance.
Since .let
internally copies a reference in the same way, it is preserving that reference even if the demo
property is changed to point at something else or null
.