Home > OS >  Synchronized statement having a variable of type Any() as a argument. (Android)(Kotlin)
Synchronized statement having a variable of type Any() as a argument. (Android)(Kotlin)

Time:11-18

In the synchronized statement parameter what does a variable Lock = Any() even do? synchronized only allow a single thread into the body at a given time, hence why don't we use this instead of Lock = Any() as this refers to the companion object itself?

I have read that it is something related to java monitors, however, I don't get it.

companion object{
...
private val LOCK = Any()

operator fun invoke(context: Context) = instance ?:synchronized(LOCK){
...
    }
        }

CodePudding user response:

Have a look at the Java docs on locks and synchronisation. (If you're only familiar with Kotlin, Java's what it's based on and it's close enough to get the idea)

Basically, synchronisation allows you to control access to certain things, by limiting it to one thread at a time. Each object has an intrinsic lock (or monitor) which, if you make use of it, only one thread can acquire at a time. So you can use that to turn an object into a kind of access token - if another thread wants to acquire that lock, it has to wait until the first thread is done with it.

So you can use that to block execution of bits of code, by gating it behind these access tokens. If you use a plain synchronized method (without specifying a token), or if you use a synchronized statement with this, in both cases it uses the containing object's lock to control access to the code in that method/block. (This doesn't prevent threads from accessing non-synchronised code, like other methods, or the code before a synchronised block.)


But this is limiting - it means that everything that accesses any synchronised code inside that object instance needs to acquire the same token. If you have multiple synchronised blocks that are really separate from each other, and could safely be run concurrently, then sharing a lock is inefficient - it's holding up other threads for no good reason.

But if you have a separate lock object for each, then acquiring one for task A doesn't interfere with acquiring the one for task B. By using separate locks, you can make your synchronisation more granular. You still have to be careful though - if task A actually involves calling stuff in task B, and vice versa, you can end up with a situation where both threads are blocked, waiting for the other to complete. In that case both tasks should really use a shared lock, so whatever starts a task is guaranteed to finish it.


As for why this particular example (with one synchronised block) uses an explicit locking object instead of this, I'm not sure. One other benefit of a separate locking object is that it can be shared between different instances of a class, or even different classes, if they all do work where they could interfere with each other. Since a companion object is basically a single, kinda static object, you'd get that benefit by using it as the lock

And the LOCK object is private, so it can't be used as a lock by other classes - it's purely used internally here. It could be accessed by synchronised blocks in the class itself (through their instance methods) and maybe the coder thought synchronized(LOCK) reads better than synchronized(Companion). Maybe they're planning to add other granular locks later, or maybe they're just used to explicitly creating locking objects so you're always encouraged to think about what you're locking on.

Maybe there's some other reason you might want to do it this way in Kotlin, but hopefully that gives you some ideas about why you'd create different locking objects in the first place

  • Related