Hi, question about Object singleton. Can multiple ...
# getting-started
n
Hi, question about Object singleton. Can multiple threads mutate integer properties inside Object singleton in thread safe manner or do we have to write explicit code to ensure properties inside Object singleton are mutated in thread safe ?
h
AFAIK only the creation of the
object
is dealt with by Kotlin. Everything inside you have to synchronize yourself.
👍 1
👍🏻 1
n
I see. So quick way would be to declare the properties as AtomicInteger instead of int ?
h
Yes. Or just add @Volatile .
n
Okay thanks
Object MyObject { var data: Int = 0 } Thread 1 performing “MyObject.data++” and thread 2 performing MyObject.data++ Will this be done in thread safe manner? @hho
k
Definitely not.
++
is not an atomic operation. It involves reading and then writing. You should consider using
AtomicInteger.incrementAndGet()
(or
getAndIncrement()
).
n
I see. Isn’t accessing MyObject is thread safe? So why will it cause an issue if mutated by multiple threads ?
k
The creation of MyObject is thread safe. Accessing its properties isn't.
j
Kotlin docs say that even @Volatile doesn’t ensure your object stays thread safe, so best option is Atomic integer
j
If you can, just don't use mutable things in an object, though
âž• 4
n
Hmm I tried a small junit test and seems to be working fine without using AtomicInteger. Strange. Am I doing anything wrong here ? object MyObject { var data: Int = 0 } class ThreadTest { @org.junit.jupiter.api.Test fun testMultipleThreadsForMetricsObject() { thread(start = true) { MyObject.data++ } thread(start = true) { MyObject.data++ } Thread.sleep(3000) Assertions.assertEquals(2, MyObject.data) } }
k
This kind of test is non-deterministic. It may appear to work for many days, and then suddenly you get a different answer. That's the nature of thread safety.
👍 1
n
How do we ensure to write deterministic tests?
k
It's very hard to write deterministic tests when testing thread safety.
👍 1
j
@Nitesh try creating 20 threads or more and you’ll see that there are irregularities.
Just do this
scope.launch { repeat(1000) { launch { obj++ } } // Observe result }
or this: scope.launch { repeat(1000) { launch { obj++ print(obj) } } }
k
Or to use just a minor change to your original test:
Copy code
fun testMultipleThreadsForMetricsObject() {
        thread(start = true) {
            repeat(100_000) {
                MyObject.data++
            }
        }
        thread(start = true) {
            repeat(100_000) {
                MyObject.data++
            }
        }
        Thread.sleep(3000)
        Assertions.assertEquals(200_000, MyObject.data)
    }
This is still not deterministic, but it will fail most of the time, with different actual values of MyObject.data each time.
j
Also it would be better to join the threads instead of just sleeping 3 seconds. This is automatically solved when using the coroutines approach above
âž• 1
w
How do we ensure to write deterministic tests?
If you want to test your concurrent algorithm, there are extremely useful frameworks for this such as Lincheck. Even after trying to implement seemingly simple concurrent algorithms, you will be baffled by how difficult it is. Lincheck named above has been used to find bugs in
java.util.concurrent.ConcurrentLinkedDeque
that are yet to be fixed by extremely smart developers. The best thing to do, is to not use mutable state like @Joffrey recommended. Otherwise always use locks, atomic operations or existing concurrent data structures and thank whatever you believe in every day that people implemented these complicated things for you 🙂