Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is Kotlin synchronized() not locking basic types?

Tags:

kotlin

class Notification(val context: Context, title: String, message: String) {
    private val channelID = "TestMessages"

    companion object ID {
        var s_notificationID = -1
    }

    init {
        var notificationID = -1
        synchronized(s_notificationID) {
            if (++s_notificationID == 0)
                createNotificationChannel()
            notificationID = s_notificationID
        }

The above is being called simultaneously from two threads. A breakpoint in createNotificationChannel() clearly showed that sometimes s_notificationID equals 1.

However, if I change synchronized(s_notificationID) to synchronized(ID) then it seems to lock fine.

Is synchronized() not locking basic types? And if so, why does it compile?

like image 877
Doron Ben-Ari Avatar asked Jan 25 '23 07:01

Doron Ben-Ari


1 Answers

A look at the generated JVM bytecode indicates that the ID example looks like

synchronized(ID) { ... }

which is what you'd expect. However, the s_notificationID example looks more like

synchronized(Integer.valueOf(s_notificationID)) { ... }

In Java, we can only synchronize on objects, not on primitives. Kotlin mostly removes this distinction, but it looks like you've found one place where the implementation still seeps through. Since s_notificationID is an int as far as the JVM is concerned (hence, not an object) but synchronized expects an object, Kotlin is "smart" enough to wrap the value in Integer.valueOf on demand. Unfortunately for you, that produces wildly inconsistent results, because

This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.

So for small numbers, this is guaranteed to lock on some cached object in memory that you don't control. For large ones, it may be a fresh object (hence always unlocked) or it might again end up on a cached object out of your hands.

The lesson here, it seems, is: Don't synchronize on primitive types.

like image 185
Silvio Mayolo Avatar answered Jun 02 '23 10:06

Silvio Mayolo