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

Why is an atomic block in Scala run twice when exception gets thrown?

代码:

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 文档 this page 的底部是这样说的:

To make nesting very cheap, ScalaSTM tries to flatten all of the nesting levels together into a single top-level transaction. If an inner transaction throws an exception then there isn’t enough information to perform the partial rollback, so ScalaSTM restarts the entire transaction in a mode that does exact nesting. This optimization is called subsumption.

那么会发生什么:

  1. 整个事情都是作为 "flattened" t运行saction
  2. 尝试的
  3. last 设置为 "outer"
  4. last 设置为 "inner"
  5. "inner" 打印
  6. 内部原子块抛出异常,外部块不抛出异常
  7. ScalaSTM 不知道如何回滚内部 t运行saction 因为它 运行 "flattened" 所以它回滚整个事情(last 是现在回到 "none") 并重试 "non-flattened"
  8. last 设置为 "outer"
  9. last 设置为 "inner"
  10. "inner 打印
  11. 内部原子块抛出异常,被外部块捕获
  12. 这次因为没有扁平化,只能回滚内部块,last设置回"outer"