There are two big categories of operations: atomic and non-atomic. Atomic operations cannot be split into smaller operations (you can't half-read a variable).
Incrementing a variable is not truly a single operation:
i++
is ‘look at what
i
is, add 1 to that, then update
i
to that’.
In sequential programming, you don't have to care about that. In concurrent programming, it becomes very important, because different threads can see the states in-between operations.
For example, let's imagine two threads (
a
and
b
) incrementing the counter variable:
•
a
reads 0
•
b
reads 0
•
a
adds one -> 1
•
b
adds one -> 1
•
a
stores 1 in counter
•
b
stores 1 in counter
In the end, we did
++
two times, but the variable only stores 1, not 2, because the threads stepped on each other. If the order was different, it's possible to get 2 as well.
This behavior is common to all languages that work across multiple cores, it's not related to Kotlin at all (
https://en.m.wikipedia.org/wiki/Linearizability)
Coroutines propose a solution for this: at all cost, avoid shared mutable state.
• “shared between threads”: if you don't share objects, they can't be edited at the same time by two threads
• “mutable state”: immutable objects can't be seen during their modification... They're not modified!