【问题标题】:Using synchronized/locks in future code在未来的代码中使用同步/锁定
【发布时间】:2013-07-24 05:50:46
【问题描述】:

我们正在使用 Scala、Play 框架和 MongoDB(使用 ReactiveMongo 作为我们的驱动程序)构建一个 Web 应用程序。应用程序架构是非阻塞端到端的。

在我们代码的某些部分,我们需要访问一些非线程安全的库,例如 Scala 的解析器组合器、Scala 的反射等。我们目前将此类调用封装在 synchronized 块中。我有两个问题:

  1. 将synchronized 与future-y 代码一起使用时有什么需要注意的地方吗?
  2. 从性能和可用性的角度来看,使用锁(例如ReentrantLock)比使用synchronized 更好吗?

【问题讨论】:

  • 我无法理解您所说的“未来代码”是什么意思
  • @fge,代码大量使用Futures。我认为从上下文(Play、Reactive* 等)来看这已经足够清楚了,但似乎并非如此,所以很抱歉。
  • 您不应使用默认执行上下文执行阻塞操作。 This answer 可能有用。

标签: java scala concurrency playframework-2.0 akka


【解决方案1】:

这是一个老问题))例如看到这里using-actors-instead-of-synchronized。简而言之,最好使用演员而不是锁:

class GreetingActor extends Actor with ActorLogging {

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

在任何给定时间只会处理一条消息,因此您可以放置​​任何您想要的非线程安全代码而不是 log.info,一切都会正常工作。顺便说一句,使用询问模式,您可以将您的演员无缝集成到需要期货的现有代码中。

【讨论】:

【解决方案2】:

对我来说,您将面临的主要问题是任何对同步或锁定代码段的调用都可能阻塞,从而使执行上下文的线程瘫痪。为避免此问题,您可以使用 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内部的串行代码中。

最后,我建议使用同步而不是锁。对于大多数应用程序(尤其是在临界区很大的情况下),性能差异并不明显。

【讨论】:

  • +1 用于阻塞()提示。同意使用synchronized,除非您想利用锁轮询、超时、可中断性等。
【解决方案3】:

你提到的例子,即反射和解析应该是相当不可变的,你不应该需要锁定,但如果你要使用锁,那么同步块就可以了。我认为使用同步与锁定之间没有太大的性能差异。

【讨论】:

    【解决方案4】:

    我认为最简单和最安全的方法是(如果可以的话)Thread Confinement。 即每个线程创建自己的解析器组合器等实例,然后使用它。

    如果您需要任何同步(应该避免这种情况,因为在流量下它将成为杀手),synchornized 或 ReentrantLock 将提供几乎相同的性能。这又取决于哪些对象需要保护哪些锁等。在 Web 应用程序中,除非绝对必要,否则不鼓励这样做。

    【讨论】:

    • 您能否提供更多关于如何实现线程限制的信息?
    猜你喜欢
    • 1970-01-01
    • 2021-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-09
    • 1970-01-01
    相关资源
    最近更新 更多