Search code examples
javamultithreadingscalajava.util.concurrent

Is it safe to use AtomicBoolean for database locking in Scala/Java?


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.


Solution

  • 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")
      }
    }