Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using synchronized/locks in future code

We are building a web app with Scala, Play framework, and MongoDB (with ReactiveMongo as our driver). The application architecture is non-blocking end to end.

In some parts of our code, we need to access some non-thread-safe libraries such as Scala's parser combinators, Scala's reflection etc. We are currently enclosing such calls in synchronized blocks. I have two questions:

  1. Are there any gotchas to look out for when using synchronized with future-y code?
  2. Is it better to use locks (such as ReentrantLock) rather than synchronized, from both performance and usability standpoint?
like image 924
missingfaktor Avatar asked Jul 24 '13 05:07

missingfaktor


People also ask

How locks are better than synchronized?

Only one thread is allowed to access only one method at any given point of time using a synchronized block. This is a very expensive operation. Locks avoid this by allowing the configuration of various locks for different purpose.

Can synchronization avoid deadlock?

Synchronize does not prevent deadlock. On the contrary, synchronizing without taking proper precautions can introduce the potential for deadlock.

Why would you want to use synchronized blocks instead of synchronized methods?

Synchronized blocks provide granular control over a lock, as you can use arbitrary any lock to provide mutual exclusion to critical section code. On the other hand, the synchronized method always locks either on the current object represented by this keyword or class level lock, if it's a static synchronized method.


2 Answers

This is an old question)) see here using-actors-instead-of-synchronized for example. In short it would be more advisable to use actors instead of locks:

class GreetingActor extends Actor with ActorLogging {

  def receive = {
    case Greeting(who) ⇒ log.info("Hello " + who) 
  }
}

only one message will be processed at any given time, so you can put any not-thread safe code you want instead of log.info, everything will work OK. BTW using ask pattern you can seamlessly integrate your actors into existing code that requires futures.

like image 122
vitalii Avatar answered Oct 10 '22 11:10

vitalii


For me the main problem you will face is that any call to a synchronized or a locked section of code may block and thus paralyze the threads of the execution context. To avoid this issue, you can wrap any call to a potentially blocking method using scala.concurrent.blocking:

import scala.concurrent._
import ExecutionContext.Implicits.global

def synchronizedMethod(s: String) = synchronized{ s.size }

val f = future{ 
   println("Hello")
   val i = blocking{  //Adjust the execution context behavior
     synchronizedMethod("Hello")
   }
   println(i)
   i
 }

Of course, it may be better to consider alternatives like thread-local variables or wrapping invocation to serial code inside an actor.

Finally, I suggest using synchronized instead of locks. For most application (especially if the critical sections are huge), the performance difference is not noticeable.

like image 30
paradigmatic Avatar answered Oct 10 '22 09:10

paradigmatic