Colton Idle
06/09/2024, 9:55 PMclass Counter {
@Volatile
private var value = 0
fun inc(): Int {
return ++value
}
fun get(): Int {
return value
}
}
Joffrey
06/09/2024, 9:57 PM++value
is not an atomic operation. It first reads the value
and then writes value + 1
back into the value
variable. Between these 2 operations, another thread could change the value, and this change would be overwritten.ephemient
06/09/2024, 10:29 PM++value
is basically syntactic sugar for
value = value.inc()
(well slightly more complex but https://kotlinlang.org/spec/kotlin-spec.html#prefix-increment-expressions has all the details)ephemient
06/09/2024, 10:31 PM++
is not thatColton Idle
06/10/2024, 4:50 AMephemient
06/10/2024, 4:55 AMget()
in other threads may be "out of date", unless you make that @Synchronized
tooephemient
06/10/2024, 5:01 AMclass Counter {
private var value = 0
@Synchronized
fun inc(): Int = ++value
fun get(): Int = value
}
val counter = Counter()
thread {
counter.inc()
}
while (counter.get() == 0) {} // might not terminate
ephemient
06/10/2024, 5:09 AMclass Counter {
private var value = 0
@Synchronized
fun inc(): Int = ++value
@Synchronized
fun get(): Int = value
}
class Counter {
@Volatile
private var value = 0
@Synchronized
fun inc(): Int = ++value
fun get(): Int = value
}
import java.util.concurrent.atomic.*
class Counter {
private val value = AtomicInteger(0)
fun inc(): Int = value.incrementAndGet()
fun get(): Int = value.intValue()
}
import java.util.concurrent.atomic.*
class Counter {
@Volatile
private var value = 0
fun inc(): Int = valueFU.incrementAndGet(this)
fun get(): Int = value
companion object {
private val valueFU = AtomicIntegerFieldUpdater(Counter::class.java, "value")
}
}
import kotlinx.atomicfu.*
class Counter {
private val value = atomic(0)
fun inc(): Int = value.incrementAndGet()
fun get(): Int = value.value
}
ephemient
06/10/2024, 5:12 AMYoussef Shoaib [MOD]
06/10/2024, 8:20 AMStephan Schröder
06/10/2024, 8:37 AMReentrantReadWriteLock
which should be more efficient than using `synchronized`:
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write
class Counter {
private var value = 0
private val lock = ReentrantReadWriteLock()
fun inc(): Int = lock.write { ++value }
fun get(): Int = lock.read { value }
}
assuming your code is for the JVM only.
More on locking on the JVM: here
Also notice that Kotlin has an extension functions withLock to work with Lock.
and read/write to work with ReentrantReadWriteLock (which I used in my example).Stephan Schröder
06/10/2024, 9:23 AMsynchronized
vs ReentrantLock
(though it doesn't mention ReentrantReadWriteLock
which I used) and this answer from StackOverflow sounds sensible, basically stating that synchronized
is a good default.ephemient
06/10/2024, 3:39 PM@Synchronized
is that it is effectively synchronized(this) { ... }
, which means that any outside code can easily introduce locking contentionephemient
06/10/2024, 3:40 PMprivate val lock = Any()
fun inc() = synchronized(lock) { ... }
fun get() = synchronized(lock) { ... }
but either way, monitorenter/monitorexit is fine in most casesephemient
06/10/2024, 3:41 PMCLOVIS
06/10/2024, 3:48 PMsynchronized(this)
would create contention whereas synchronized(lock)
wouldn't? I don't see the difference.ephemient
06/10/2024, 3:51 PMsynchronized(counter)
CLOVIS
06/10/2024, 3:51 PMephemient
06/10/2024, 3:52 PMColton Idle
06/10/2024, 6:49 PM