Home > Blockchain >  Why ?.let can prevent nullPointException in multithreading?
Why ?.let can prevent nullPointException in multithreading?

Time:01-11

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.

  • Related