I have an application where I want to ensure that a method is called at most once concurrently, say when updating user balance in a database.
I am thinking of using the following locking mechanism: (showing Scala code below, but should be similar with Java Lambdas):
object Foo{
val dbLocked = new java.util.concurrent.atomic.AtomicBoolean(false)
def usingAtoimcDB[T](f: => T):T = {
if (dbLocked.get) throw new Exception("db is locked")
dbLocked.set(true)
try f
finally dbLocked.set(false)
}
}
Is this safe to use when usingAtoimcDB
may be called concurrently?
EDIT: The corrected code below, as pointed in this answer:
def usingAtoimcDB[T](f: => T):T = {
if(dbLocked.compareAndSet(false, true)) {
//db is now locked
try f
finally dbLocked.set(false)
} else {
//db is already locked
throw new Exception("db is locked")
}
}
EDIT 2:
Using a spinloop. Is this also ok?
def usingAtoimcDB[T](f: => T):T = {
while (!dbLocked.compareAndSet(false, true)) {Thread.sleep(1)}
try f
finally dbLocked.set(false)
}
EDIT3: Based on the answers and comments below, I am also considering using queues.
Yes, it should work as espected. I would slightly modify your function using compareAndSet
call.
compareAndSet
method has the advantage to be an atomic operation - there are no race conditions and the value will be changed atomically.
def usingAtoimcDB[T](f: => T):T = {
if(dbLocked.compareAndSet(false, true)) {
//db is now locked
try f
finally dbLocked.set(false)
} else {
//db is already locked
throw new Exception("db is locked")
}
}