我们正在使用Scala,Play框架和MongoDB(使用ReactiveMongo作为我们的驱动程序)构建Web应用程序。应用程序架构是端到端的非阻塞。
在我们代码的某些部分,我们需要访问一些非线程安全的库,例如Scala的解析器组合器,Scala的反射等。我们目前正在synchronized
块中包含这些调用。我有两个问题:
synchronized
与future-y代码一起使用时,是否有任何问题需要注意?ReentrantLock
)而不是synchronized
是否更好?答案 0 :(得分:8)
这是一个老问题))请参阅此处using-actors-instead-of-synchronized。简而言之,更可取的是使用actor而不是lock:
class GreetingActor extends Actor with ActorLogging {
def receive = {
case Greeting(who) ⇒ log.info("Hello " + who)
}
}
在任何给定时间只会处理一条消息,因此您可以放置任何非线程安全的代码而不是log.info,一切正常。 BTW使用ask模式,您可以将您的演员无缝集成到需要未来的现有代码中。
答案 1 :(得分:3)
对我来说,您将遇到的主要问题是对同步或锁定的代码段的任何调用都可能阻塞并因此使执行上下文的线程瘫痪。要避免此问题,您可以使用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
}
当然,最好考虑线程局部变量等替代方法或将调用包装到actor内的串行代码中。
最后,我建议使用synchronized而不是lock。对于大多数应用(特别是如果关键部分很大),性能差异并不明显。
答案 2 :(得分:2)
你提到的例子,即反射和解析应该是合理不可变的,你不需要锁定,但如果你要使用锁,那么同步块就可以了。我不认为使用synchronized和Lock之间存在很大的性能差异。
答案 3 :(得分:2)
我认为最简单,最安全的方式是(如果可以的话)来自线程限制。 即每个线程创建自己的解析器组合器等实例,然后使用它。
如果您需要任何同步(应该避免在流量下它将成为杀手),synchornized
或ReentrantLock
将提供几乎相同的性能。它还取决于需要保护哪些对象锁定等等。在Web应用程序中,除非绝对必要,否则不鼓励它。