在他的视频中(关于Scala的惰性求值,即
lazy
关键字),Martin Odersky展示了以下用于构造Stream
的cons
操作的实现:def cons[T](hd: T, tl: => Stream[T]) = new Stream[T] {
def head = hd
lazy val tail = tl
...
}
因此,在该语言中,tail
操作利用惰性求值特性被简洁地编写。
但实际上(在Scala 2.11.7中),tail
的实现有点不太优雅:
@volatile private[this] var tlVal: Stream[A] = _
@volatile private[this] var tlGen = tl _
def tailDefined: Boolean = tlGen eq null
override def tail: Stream[A] = {
if (!tailDefined)
synchronized {
if (!tailDefined) {
tlVal = tlGen()
tlGen = null
}
}
tlVal
}
在Java中,双重检查锁和两个volatile字段是实现线程安全的延迟计算的常见方式。
那么问题是:
- 在多线程情况下,Scala的
lazy
关键字是否提供了“最多评估一次”的保证? - 在Scala中,实际的
tail
实现模式是否是执行线程安全的延迟计算的惯用方式?
lazy val
。它们保证在第一次访问时只被评估一次。 - Jasper-M