为什么在抛出异常时Scala中的原子块会运行两次?

为什么在抛出异常时Scala中的原子块会运行两次?,scala,exception,atomic,stm,Scala,Exception,Atomic,Stm,代码: object Blub { import scala.concurrent.stm._ val last = Ref("none") def bla() = atomic { implicit txn => last() = "outer" try { atomic { implicit txn => last() = "inner" println(last()) throw new

代码:

object Blub {
  import scala.concurrent.stm._
  val last = Ref("none")

  def bla() = atomic { implicit txn =>
    last() = "outer"
    try {
      atomic { implicit txn =>
        last() = "inner"
        println(last())
        throw new RuntimeException
      }
    } catch {
      case _: RuntimeException =>
    }
  }

  def main(args: Array[String]): Unit = {
    bla()
    println("Result: "+last.single())
  }
}
inner  
inner  
Result: outer
输出:

object Blub {
  import scala.concurrent.stm._
  val last = Ref("none")

  def bla() = atomic { implicit txn =>
    last() = "outer"
    try {
      atomic { implicit txn =>
        last() = "inner"
        println(last())
        throw new RuntimeException
      }
    } catch {
      case _: RuntimeException =>
    }
  }

  def main(args: Array[String]): Unit = {
    bla()
    println("Result: "+last.single())
  }
}
inner  
inner  
Result: outer
有人能解释为什么内部原子块运行两次吗?我知道它会由于异常而进行回滚,从而得到最终结果。但是我不明白为什么它会再次运行代码。

ScalaSTM文档的底部有这样一句话:

为了使嵌套非常便宜,ScalaSTM尝试将所有的 将级别嵌套到单个顶级事务中。如果 内部事务引发异常,则没有足够的 执行部分回滚的信息,以便ScalaSTM重新启动 在进行精确嵌套的模式下执行整个事务。这 优化称为包容

因此发生的情况是:

  • 整个事件都是作为“扁平化”事务进行尝试的
  • last
    设置为
    “外部”
  • last
    设置为
    “内部”
  • 打印“内部”
  • 内部原子块抛出异常,外部块不抛出异常
  • ScalaSTM不知道如何仅回滚内部事务,因为它运行了“展平”,所以它回滚整个事务(
    last
    现在返回到
    “none”
    ),并重试“非展平”
  • last
    设置为
    “外部”
  • last
    设置为
    “内部”
  • “内部
    已打印
  • 内部原子块抛出一个异常,该异常被外部块捕获
  • 这一次因为它是非展平的,所以它只能回滚内部块,
    last
    被设置回
    “outer”